mirror of
https://github.com/sebastian-heinz/mhf-server.git
synced 2025-04-02 12:28:27 +08:00
initial commit
This commit is contained in:
commit
4c58741d2f
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
||||
###############################
|
||||
# Core EditorConfig Options #
|
||||
###############################
|
||||
|
||||
root = true
|
||||
|
||||
# All files
|
||||
[*]
|
||||
indent_style = space
|
||||
|
||||
# Code files
|
||||
[*.cs]
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
18
.gitattributes
vendored
Normal file
18
.gitattributes
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
#* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
#*.c text
|
||||
#*.h text
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
#*.sln text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.DAT binary
|
||||
*.ico binary
|
||||
*.jpg binary
|
||||
*.exe binary
|
||||
*.dll binary
|
||||
*.pfx binary
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please describe the idea.**
|
||||
A clear and concise description of the features functionality is. Ex. I'd like to have a command that [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
13
.github/workflows/build.yaml
vendored
Normal file
13
.github/workflows/build.yaml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
name: Build
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.0.100
|
||||
- name: Build Project
|
||||
run: dotnet build --configuration Release
|
85
.github/workflows/release.yml
vendored
Normal file
85
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
name: Create Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.0.100
|
||||
|
||||
- name: Build Release
|
||||
id: build_release
|
||||
run: |
|
||||
MHF_VERSION_FILE=${{ github.workspace }}/mhf.version
|
||||
MHF_VERSION=$(<"$MHF_VERSION_FILE")
|
||||
echo ::set-env name=MHF_VERSION::$MHF_VERSION
|
||||
echo ::set-env name=MHF_VERSION_E::$(echo ${GITHUB_SHA} | cut -c1-8)
|
||||
mkdir ./release
|
||||
for MHF_RUNTIME in win-x86 win-x64 linux-x64 osx-x64; do
|
||||
# Server
|
||||
dotnet publish Mhf.Cli/Mhf.Cli.csproj /p:Version=$MHF_VERSION /p:FromMSBuild=true --runtime $MHF_RUNTIME --configuration Release --output ./publish/$MHF_RUNTIME-$MHF_VERSION/Server
|
||||
# ReleaseFiles
|
||||
cp -r ./ReleaseFiles/. ./publish/$MHF_RUNTIME-$MHF_VERSION/
|
||||
# Pack
|
||||
tar cjf ./release/$MHF_RUNTIME-$MHF_VERSION.tar.gz ./publish/$MHF_RUNTIME-$MHF_VERSION
|
||||
done
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: release-${{ env.MHF_VERSION }}-${{ env.MHF_VERSION_E }}
|
||||
release_name: Release ${{ env.MHF_VERSION }}-${{ env.MHF_VERSION_E }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload win-x86 Release Asset
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./release/win-x86-${{ env.MHF_VERSION }}.tar.gz
|
||||
asset_name: win-x86-${{ env.MHF_VERSION }}-${{ env.MHF_VERSION_E }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: Upload win-x64 Release Asset
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./release/win-x64-${{ env.MHF_VERSION }}.tar.gz
|
||||
asset_name: win-x64-${{ env.MHF_VERSION }}-${{ env.MHF_VERSION_E }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: Upload linux-x64 Release Asset
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./release/linux-x64-${{ env.MHF_VERSION }}.tar.gz
|
||||
asset_name: linux-x64-${{ env.MHF_VERSION }}-${{ env.MHF_VERSION_E }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: Upload osx-x64 Release Asset
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./release/osx-x64-${{ env.MHF_VERSION }}.tar.gz
|
||||
asset_name: osx-x64-${{ env.MHF_VERSION }}-${{ env.MHF_VERSION_E }}.tar.gz
|
||||
asset_content_type: application/gzip
|
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
## Folders specific
|
||||
/publish/
|
||||
/release/
|
||||
/packages/
|
||||
|
||||
## Folders anywhere
|
||||
.idea/
|
||||
.vs/
|
||||
|
||||
## Files anywhere
|
||||
*.DS_Store
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
674
LICENSE
Normal file
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 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 General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is 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. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
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.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
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 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. Use with the GNU Affero General Public License.
|
||||
|
||||
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 Affero 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 special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU 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 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 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 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 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
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 GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
30
Mhf.Cli/Argument/ConsoleParameter.cs
Normal file
30
Mhf.Cli/Argument/ConsoleParameter.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mhf.Cli.Argument
|
||||
{
|
||||
public class ConsoleParameter
|
||||
{
|
||||
public ConsoleParameter(string key)
|
||||
{
|
||||
Key = key;
|
||||
Arguments = new List<string>();
|
||||
Switches = new List<string>();
|
||||
ArgumentMap = new Dictionary<string, string>();
|
||||
SwitchMap = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public string Key { get; }
|
||||
public List<string> Arguments { get; }
|
||||
public List<string> Switches { get; }
|
||||
public Dictionary<string, string> SwitchMap { get; }
|
||||
public Dictionary<string, string> ArgumentMap { get; }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Arguments.Clear();
|
||||
Switches.Clear();
|
||||
ArgumentMap.Clear();
|
||||
SwitchMap.Clear();
|
||||
}
|
||||
}
|
||||
}
|
9
Mhf.Cli/Argument/ISwitchConsumer.cs
Normal file
9
Mhf.Cli/Argument/ISwitchConsumer.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mhf.Cli.Argument
|
||||
{
|
||||
public interface ISwitchConsumer
|
||||
{
|
||||
List<ISwitchProperty> Switches { get; }
|
||||
}
|
||||
}
|
10
Mhf.Cli/Argument/ISwitchProperty.cs
Normal file
10
Mhf.Cli/Argument/ISwitchProperty.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Mhf.Cli.Argument
|
||||
{
|
||||
public interface ISwitchProperty
|
||||
{
|
||||
string Key { get; }
|
||||
string Description { get; }
|
||||
string ValueDescription { get; }
|
||||
bool Assign(string value);
|
||||
}
|
||||
}
|
48
Mhf.Cli/Argument/SwitchProperty.cs
Normal file
48
Mhf.Cli/Argument/SwitchProperty.cs
Normal file
@ -0,0 +1,48 @@
|
||||
namespace Mhf.Cli.Argument
|
||||
{
|
||||
public class SwitchProperty<T> : ISwitchProperty
|
||||
{
|
||||
public static TryParseHandler NoOp = (string value, out T result) =>
|
||||
{
|
||||
result = default;
|
||||
return true;
|
||||
};
|
||||
|
||||
private TryParseHandler _parser;
|
||||
private AssignHandler _assigner;
|
||||
|
||||
public SwitchProperty(string key, string valueDescription, string description, TryParseHandler parser,
|
||||
AssignHandler assigner)
|
||||
{
|
||||
Key = key;
|
||||
ValueDescription = valueDescription;
|
||||
Description = description;
|
||||
_parser = parser;
|
||||
_assigner = assigner;
|
||||
}
|
||||
|
||||
public delegate bool TryParseHandler(string value, out T result);
|
||||
|
||||
public delegate void AssignHandler(T result);
|
||||
|
||||
public string Key { get; }
|
||||
public string Description { get; }
|
||||
public string ValueDescription { get; }
|
||||
|
||||
public bool Assign(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_parser(value, out T result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_assigner(result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
9
Mhf.Cli/Command/CommandResultType.cs
Normal file
9
Mhf.Cli/Command/CommandResultType.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Mhf.Cli.Command
|
||||
{
|
||||
public enum CommandResultType
|
||||
{
|
||||
Exit,
|
||||
Continue,
|
||||
Completed
|
||||
}
|
||||
}
|
19
Mhf.Cli/Command/Commands/DecryptCommand.cs
Normal file
19
Mhf.Cli/Command/Commands/DecryptCommand.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Mhf.Cli.Argument;
|
||||
using Mhf.Server.Packet;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Cli.Command.Commands
|
||||
{
|
||||
public class DecryptCommand : ConsoleCommand
|
||||
{
|
||||
public override CommandResultType Handle(ConsoleParameter parameter)
|
||||
{
|
||||
PacketFactory pf = new PacketFactory(new MhfSetting());
|
||||
pf.Test();
|
||||
return CommandResultType.Completed;
|
||||
}
|
||||
|
||||
public override string Key => "decrypt";
|
||||
public override string Description => "Decrypt packet data";
|
||||
}
|
||||
}
|
16
Mhf.Cli/Command/Commands/ExitCommand.cs
Normal file
16
Mhf.Cli/Command/Commands/ExitCommand.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Mhf.Cli.Argument;
|
||||
|
||||
namespace Mhf.Cli.Command.Commands
|
||||
{
|
||||
public class ExitCommand : ConsoleCommand
|
||||
{
|
||||
public override CommandResultType Handle(ConsoleParameter parameter)
|
||||
{
|
||||
Logger.Info("Exiting...");
|
||||
return CommandResultType.Exit;
|
||||
}
|
||||
|
||||
public override string Key => "exit";
|
||||
public override string Description => "Closes the program";
|
||||
}
|
||||
}
|
67
Mhf.Cli/Command/Commands/HelpCommand.cs
Normal file
67
Mhf.Cli/Command/Commands/HelpCommand.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Mhf.Cli.Argument;
|
||||
|
||||
namespace Mhf.Cli.Command.Commands
|
||||
{
|
||||
public class HelpCommand : ConsoleCommand
|
||||
{
|
||||
private readonly Dictionary<string, IConsoleCommand> _commands;
|
||||
|
||||
public HelpCommand(Dictionary<string, IConsoleCommand> commands)
|
||||
{
|
||||
_commands = commands;
|
||||
}
|
||||
|
||||
public override CommandResultType Handle(ConsoleParameter parameter)
|
||||
{
|
||||
if (parameter.Arguments.Count >= 1)
|
||||
{
|
||||
string subKey = parameter.Arguments[0];
|
||||
if (!_commands.ContainsKey(subKey))
|
||||
{
|
||||
Logger.Error(
|
||||
$"Command: 'help {subKey}' not available. Type 'help' for a list of available commands.");
|
||||
return CommandResultType.Continue;
|
||||
}
|
||||
|
||||
IConsoleCommand consoleCommandHelp = _commands[subKey];
|
||||
Logger.Info(ShowHelp(consoleCommandHelp));
|
||||
return CommandResultType.Continue;
|
||||
}
|
||||
|
||||
ShowHelp();
|
||||
return CommandResultType.Continue;
|
||||
}
|
||||
|
||||
private void ShowHelp()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("Available Commands:");
|
||||
foreach (string key in _commands.Keys)
|
||||
{
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("----------");
|
||||
IConsoleCommand command = _commands[key];
|
||||
sb.Append(ShowHelp(command));
|
||||
}
|
||||
|
||||
Logger.Info(sb.ToString());
|
||||
}
|
||||
|
||||
private string ShowHelp(IConsoleCommand command)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(command.Key);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append($"- {command.Description}");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override string Key => "help";
|
||||
public override string Description => "Displays this text";
|
||||
}
|
||||
}
|
55
Mhf.Cli/Command/Commands/ServerCommand.cs
Normal file
55
Mhf.Cli/Command/Commands/ServerCommand.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Mhf.Cli.Argument;
|
||||
using Mhf.Server;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Cli.Command.Commands
|
||||
{
|
||||
public class ServerCommand : ConsoleCommand
|
||||
{
|
||||
private MhfServer _server;
|
||||
private readonly LogWriter _logWriter;
|
||||
|
||||
public ServerCommand(LogWriter logWriter)
|
||||
{
|
||||
_logWriter = logWriter;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (_server != null)
|
||||
{
|
||||
_server.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public override CommandResultType Handle(ConsoleParameter parameter)
|
||||
{
|
||||
if (_server == null)
|
||||
{
|
||||
MhfSetting setting = new MhfSetting();
|
||||
_server = new MhfServer(setting);
|
||||
}
|
||||
|
||||
if (parameter.Arguments.Contains("start"))
|
||||
{
|
||||
_server.Start();
|
||||
return CommandResultType.Completed;
|
||||
}
|
||||
|
||||
if (parameter.Arguments.Contains("stop"))
|
||||
{
|
||||
_server.Stop();
|
||||
return CommandResultType.Completed;
|
||||
}
|
||||
|
||||
return CommandResultType.Continue;
|
||||
}
|
||||
|
||||
public override string Key => "server";
|
||||
|
||||
|
||||
public override string Description =>
|
||||
$"Monster Hunter Frontier Z Online Server. Ex.:{Environment.NewLine}server start{Environment.NewLine}server stop";
|
||||
}
|
||||
}
|
98
Mhf.Cli/Command/Commands/ShowCommand.cs
Normal file
98
Mhf.Cli/Command/Commands/ShowCommand.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Mhf.Cli.Argument;
|
||||
|
||||
namespace Mhf.Cli.Command.Commands
|
||||
{
|
||||
public class ShowCommand : ConsoleCommand
|
||||
{
|
||||
public override CommandResultType Handle(ConsoleParameter parameter)
|
||||
{
|
||||
if (parameter.Arguments.Contains("w"))
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("15. Disclaimer of Warranty.");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("ALL NECESSARY SERVICING, REPAIR OR CORRECTION.");
|
||||
sb.Append(Environment.NewLine);
|
||||
Logger.Info(sb.ToString());
|
||||
return CommandResultType.Completed;
|
||||
}
|
||||
|
||||
if (parameter.Arguments.Contains("c"))
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("2. Basic Permissions.");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("All rights granted under this License are granted for the term of");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("copyright on the Program, and are irrevocable provided the stated");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("conditions are met. This License explicitly affirms your unlimited");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("permission to run the unmodified Program. The output from running a");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("covered work is covered by this License only if the output, given its");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("content, constitutes a covered work. This License acknowledges your");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("rights of fair use or other equivalent, as provided by copyright law.");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("You may make, run and propagate covered works that you do not");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("convey, without conditions so long as your license otherwise remains");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("in force. You may convey covered works to others for the sole purpose");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("of having them make modifications exclusively for you, or provide you");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("with facilities for running those works, provided that you comply with");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("the terms of this License in conveying all material for which you do");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("not control copyright. Those thus making or running the covered works");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("for you must do so exclusively on your behalf, under your direction");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("and control, on terms that prohibit them from making any copies of");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("your copyrighted material outside their relationship with you.");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("Conveying under any other circumstances is permitted solely under");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("the conditions stated below. Sublicensing is not allowed; section 10");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("makes it unnecessary.");
|
||||
sb.Append(Environment.NewLine);
|
||||
Logger.Info(sb.ToString());
|
||||
return CommandResultType.Completed;
|
||||
}
|
||||
|
||||
return CommandResultType.Continue;
|
||||
}
|
||||
|
||||
public override string Key => "show";
|
||||
|
||||
public override string Description =>
|
||||
$"Shows Copyright. Ex.:{Environment.NewLine}show w{Environment.NewLine}show c";
|
||||
}
|
||||
}
|
103
Mhf.Cli/Command/Commands/SwitchCommand.cs
Normal file
103
Mhf.Cli/Command/Commands/SwitchCommand.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Mhf.Cli.Argument;
|
||||
|
||||
namespace Mhf.Cli.Command.Commands
|
||||
{
|
||||
public class SwitchCommand : ConsoleCommand
|
||||
{
|
||||
private readonly List<ISwitchConsumer> _parameterConsumers;
|
||||
|
||||
public SwitchCommand(List<ISwitchConsumer> parameterConsumers)
|
||||
{
|
||||
_parameterConsumers = parameterConsumers;
|
||||
}
|
||||
|
||||
public override CommandResultType Handle(ConsoleParameter parameter)
|
||||
{
|
||||
foreach (string key in parameter.SwitchMap.Keys)
|
||||
{
|
||||
ISwitchProperty property = FindSwitch(key);
|
||||
if (property == null)
|
||||
{
|
||||
Logger.Error($"Switch '{key}' not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = parameter.SwitchMap[key];
|
||||
if (!property.Assign(value))
|
||||
{
|
||||
Logger.Error($"Switch '{key}' failed, value: '{value}' is invalid");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info($"Applied {key}={value}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string booleanSwitch in parameter.Switches)
|
||||
{
|
||||
ISwitchProperty property = FindSwitch(booleanSwitch);
|
||||
if (property == null)
|
||||
{
|
||||
Logger.Error($"Switch '{booleanSwitch}' not found");
|
||||
continue;
|
||||
}
|
||||
if (!property.Assign(bool.TrueString))
|
||||
{
|
||||
Logger.Error($"Switch '{booleanSwitch}' failed, value: '{bool.TrueString}' is invalid");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info($"Applied {booleanSwitch}={bool.TrueString}");
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResultType.Completed;
|
||||
}
|
||||
|
||||
private ISwitchProperty FindSwitch(string key)
|
||||
{
|
||||
foreach (ISwitchConsumer consumer in _parameterConsumers)
|
||||
{
|
||||
foreach (ISwitchProperty property in consumer.Switches)
|
||||
{
|
||||
if (property.Key == key)
|
||||
{
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string ShowSwitches()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("Available Switches:");
|
||||
sb.Append(Environment.NewLine);
|
||||
foreach (ISwitchConsumer consumer in _parameterConsumers)
|
||||
{
|
||||
foreach (ISwitchProperty property in consumer.Switches)
|
||||
{
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(property.Key);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("> Ex.: ");
|
||||
sb.Append(property.ValueDescription);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("> ");
|
||||
sb.Append(property.Description);
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override string Key => "switch";
|
||||
public override string Description => $"Changes configuration switches{Environment.NewLine}{ShowSwitches()}";
|
||||
}
|
||||
}
|
23
Mhf.Cli/Command/ConsoleCommand.cs
Normal file
23
Mhf.Cli/Command/ConsoleCommand.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Cli.Argument;
|
||||
|
||||
namespace Mhf.Cli.Command
|
||||
{
|
||||
public abstract class ConsoleCommand : IConsoleCommand
|
||||
{
|
||||
protected ConsoleCommand()
|
||||
{
|
||||
Logger = LogProvider.Logger(this);
|
||||
}
|
||||
|
||||
protected readonly ILogger Logger;
|
||||
|
||||
public abstract string Key { get; }
|
||||
public abstract string Description { get; }
|
||||
public abstract CommandResultType Handle(ConsoleParameter parameter);
|
||||
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
12
Mhf.Cli/Command/IConsoleCommand.cs
Normal file
12
Mhf.Cli/Command/IConsoleCommand.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Mhf.Cli.Argument;
|
||||
|
||||
namespace Mhf.Cli.Command
|
||||
{
|
||||
public interface IConsoleCommand
|
||||
{
|
||||
CommandResultType Handle(ConsoleParameter parameter);
|
||||
void Shutdown();
|
||||
string Key { get; }
|
||||
string Description { get; }
|
||||
}
|
||||
}
|
367
Mhf.Cli/LogWriter.cs
Normal file
367
Mhf.Cli/LogWriter.cs
Normal file
@ -0,0 +1,367 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Arrowgene.Services.Buffers;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Cli.Argument;
|
||||
using Mhf.Server.Logging;
|
||||
|
||||
namespace Mhf.Cli
|
||||
{
|
||||
public class LogWriter : ISwitchConsumer
|
||||
{
|
||||
private readonly object _consoleLock;
|
||||
private readonly HashSet<ushort> _packetIdWhitelist;
|
||||
private readonly HashSet<ushort> _packetIdBlacklist;
|
||||
private readonly ILogger _logger;
|
||||
private readonly Queue<Log> _logQueue;
|
||||
private bool _paused;
|
||||
private bool _continueing;
|
||||
|
||||
public LogWriter()
|
||||
{
|
||||
_logger = LogProvider.Logger(this);
|
||||
_packetIdWhitelist = new HashSet<ushort>();
|
||||
_packetIdBlacklist = new HashSet<ushort>();
|
||||
_logQueue = new Queue<Log>();
|
||||
_consoleLock = new object();
|
||||
Switches = new List<ISwitchProperty>();
|
||||
_paused = false;
|
||||
_continueing = false;
|
||||
Reset();
|
||||
LoadSwitches();
|
||||
LogProvider.GlobalLogWrite += LogProviderOnGlobalLogWrite;
|
||||
}
|
||||
|
||||
public List<ISwitchProperty> Switches { get; }
|
||||
|
||||
/// <summary>
|
||||
/// --max-packet-size=64
|
||||
/// </summary>
|
||||
public int MaxPacketSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// --no-data=true
|
||||
/// </summary>
|
||||
public bool NoData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// --log-level=2
|
||||
/// </summary>
|
||||
public int MinLogLevel { get; set; }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
MaxPacketSize = -1;
|
||||
NoData = false;
|
||||
MinLogLevel = (int) LogLevel.Debug;
|
||||
_packetIdWhitelist.Clear();
|
||||
_packetIdBlacklist.Clear();
|
||||
}
|
||||
|
||||
public void WhitelistPacket(ushort packetId)
|
||||
{
|
||||
if (_packetIdWhitelist.Contains(packetId))
|
||||
{
|
||||
_logger.Error($"PacketId:{packetId} is already whitelisted");
|
||||
return;
|
||||
}
|
||||
|
||||
_packetIdWhitelist.Add(packetId);
|
||||
}
|
||||
|
||||
public void BlacklistPacket(ushort packetId)
|
||||
{
|
||||
if (_packetIdBlacklist.Contains(packetId))
|
||||
{
|
||||
_logger.Error($"PacketId:{packetId} is already blacklisted");
|
||||
return;
|
||||
}
|
||||
|
||||
_packetIdBlacklist.Add(packetId);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
_paused = true;
|
||||
}
|
||||
|
||||
public void Continue()
|
||||
{
|
||||
_continueing = true;
|
||||
while (_logQueue.TryDequeue(out Log log))
|
||||
{
|
||||
WriteLog(log);
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_continueing = false;
|
||||
}
|
||||
|
||||
private void LoadSwitches()
|
||||
{
|
||||
Switches.Add(
|
||||
new SwitchProperty<bool>(
|
||||
"--no-data",
|
||||
"--no-data=true (true|false)",
|
||||
"Don't display packet data",
|
||||
bool.TryParse,
|
||||
(result => NoData = result)
|
||||
)
|
||||
);
|
||||
Switches.Add(
|
||||
new SwitchProperty<int>(
|
||||
"--max-packet-size",
|
||||
"--max-packet-size=64 (integer)",
|
||||
"Don't display packet data",
|
||||
int.TryParse,
|
||||
(result => MaxPacketSize = result)
|
||||
)
|
||||
);
|
||||
Switches.Add(
|
||||
new SwitchProperty<int>(
|
||||
"--log-level",
|
||||
"--log-level=20 (integer) [Debug=10, Info=20, Error=30]",
|
||||
"Only display logs of the same level or above",
|
||||
int.TryParse,
|
||||
(result => MinLogLevel = result)
|
||||
)
|
||||
);
|
||||
Switches.Add(
|
||||
new SwitchProperty<object>(
|
||||
"--clear",
|
||||
"--clear",
|
||||
"Resets all switches to default",
|
||||
SwitchProperty<object>.NoOp,
|
||||
result => Reset()
|
||||
)
|
||||
);
|
||||
Switches.Add(
|
||||
new SwitchProperty<List<ushort>>(
|
||||
"--b-list",
|
||||
"--b-list=1000,2000,0xAA (PacketId[0xA|10])",
|
||||
"A blacklist that does not logs packets specified",
|
||||
TryParsePacketIdList,
|
||||
results => { AssinPacketIdList(results, BlacklistPacket); }
|
||||
)
|
||||
);
|
||||
Switches.Add(
|
||||
new SwitchProperty<List<ushort>>(
|
||||
"--w-list",
|
||||
"--w-list=1000,2000,0xAA (PacketId[0xA|10])",
|
||||
"A whitelist that only logs packets specified",
|
||||
TryParsePacketIdList,
|
||||
results => { AssinPacketIdList(results, WhitelistPacket); }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// parses strings like "1:1000,2000,3:4000" into ServerType and PacketId
|
||||
/// </summary>
|
||||
private bool TryParsePacketIdList(string value, out List<ushort> result)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
string[] values = value.Split(",");
|
||||
|
||||
result = new List<ushort>();
|
||||
foreach (string entry in values)
|
||||
{
|
||||
NumberStyles numberStyles;
|
||||
String entryStr;
|
||||
if (entry.StartsWith("0x"))
|
||||
{
|
||||
entryStr = entry.Substring(2);
|
||||
numberStyles = NumberStyles.HexNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
entryStr = entry;
|
||||
numberStyles = NumberStyles.Integer;
|
||||
}
|
||||
|
||||
|
||||
if (!ushort.TryParse(entryStr, numberStyles, null, out ushort val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result.Add(val);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AssinPacketIdList(List<ushort> results, Action<ushort> addToPacketList)
|
||||
{
|
||||
foreach (ushort entry in results)
|
||||
{
|
||||
addToPacketList(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogProviderOnGlobalLogWrite(object sender, LogWriteEventArgs logWriteEventArgs)
|
||||
{
|
||||
while (_continueing)
|
||||
{
|
||||
Thread.Sleep(10000);
|
||||
}
|
||||
|
||||
if (_paused)
|
||||
{
|
||||
_logQueue.Enqueue(logWriteEventArgs.Log);
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLog(logWriteEventArgs.Log);
|
||||
}
|
||||
|
||||
private void WriteLog(Log log)
|
||||
{
|
||||
ConsoleColor consoleColor;
|
||||
string text;
|
||||
|
||||
object tag = log.Tag;
|
||||
if (tag is MhfLogPacket logPacket)
|
||||
{
|
||||
switch (logPacket.LogType)
|
||||
{
|
||||
case MhfLogType.PacketIn:
|
||||
consoleColor = ConsoleColor.Green;
|
||||
break;
|
||||
case MhfLogType.PacketOut:
|
||||
consoleColor = ConsoleColor.Blue;
|
||||
break;
|
||||
case MhfLogType.PacketUnhandled:
|
||||
consoleColor = ConsoleColor.Red;
|
||||
break;
|
||||
default:
|
||||
consoleColor = ConsoleColor.Gray;
|
||||
break;
|
||||
}
|
||||
|
||||
text = CreatePacketLog(logPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogLevel logLevel = log.LogLevel;
|
||||
if ((int) logLevel < MinLogLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Debug:
|
||||
consoleColor = ConsoleColor.DarkCyan;
|
||||
break;
|
||||
case LogLevel.Info:
|
||||
consoleColor = ConsoleColor.Cyan;
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
consoleColor = ConsoleColor.Red;
|
||||
break;
|
||||
default:
|
||||
consoleColor = ConsoleColor.Gray;
|
||||
break;
|
||||
}
|
||||
|
||||
text = log.ToString();
|
||||
}
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_consoleLock)
|
||||
{
|
||||
Console.ForegroundColor = consoleColor;
|
||||
Console.WriteLine(text);
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExcludeLog(ushort packetId)
|
||||
{
|
||||
bool useWhitelist = _packetIdWhitelist.Count > 0;
|
||||
bool whitelisted = _packetIdWhitelist.Contains(packetId);
|
||||
bool blacklisted = _packetIdBlacklist.Contains(packetId);
|
||||
|
||||
if (useWhitelist && whitelisted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (useWhitelist)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (blacklisted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string CreatePacketLog(MhfLogPacket logPacket)
|
||||
{
|
||||
ushort packetId = logPacket.Id;
|
||||
|
||||
if (ExcludeLog(packetId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int dataSize = logPacket.Data.Size;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append($"{logPacket.ClientIdentity} Packet Log");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("----------");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append($"[{logPacket.TimeStamp:HH:mm:ss}][Typ:{logPacket.LogType}]");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(
|
||||
$"[Id:0x{logPacket.Id:X2}|{logPacket.Id}][BodyLen:{logPacket.Data.Size}][{logPacket.PacketIdName}]");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(logPacket.Header.ToLogText());
|
||||
sb.Append(Environment.NewLine);
|
||||
|
||||
if (!NoData)
|
||||
{
|
||||
IBuffer data = logPacket.Data;
|
||||
int maxPacketSize = MaxPacketSize;
|
||||
if (maxPacketSize > 0 && dataSize > maxPacketSize)
|
||||
{
|
||||
data = data.Clone(0, maxPacketSize);
|
||||
|
||||
sb.Append($"- Truncated Data showing {maxPacketSize} of {dataSize} bytes");
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
|
||||
sb.Append("ASCII:");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(data.ToAsciiString(true));
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("HEX:");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(data.ToHexString(' '));
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
|
||||
sb.Append("----------");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
16
Mhf.Cli/Mhf.Cli.csproj
Normal file
16
Mhf.Cli/Mhf.Cli.csproj
Normal file
@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<RootNamespace>Mhf.Cli</RootNamespace>
|
||||
<Title>Monster Hunter Frontier Z Command Line Interface</Title>
|
||||
<Company>Mhf Team</Company>
|
||||
<Product>Mhf.Cli</Product>
|
||||
<Version>$(Version)</Version>
|
||||
<Copyright>Copyright © 2019 Mhf Team</Copyright>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Mhf.Server\Mhf.Server.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
383
Mhf.Cli/Program.cs
Normal file
383
Mhf.Cli/Program.cs
Normal file
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* This file is part of Mhf.Cli
|
||||
*
|
||||
* Mhf.Server is a server implementation for the game "Monster Hunter Frontier Z".
|
||||
* Copyright (C) 2019-2020 Mhf Team
|
||||
*
|
||||
* Github: https://github.com/sebastian-heinz/mhf-server
|
||||
*
|
||||
* Mhf.Server is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mhf.Server 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mhf.Server. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Cli.Argument;
|
||||
using Mhf.Cli.Command;
|
||||
using Mhf.Cli.Command.Commands;
|
||||
using Mhf.Server.Common;
|
||||
|
||||
namespace Mhf.Cli
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public const char CliSeparator = ' ';
|
||||
public const char CliValueSeparator = '=';
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Program started");
|
||||
Program program = new Program();
|
||||
if (args.Length > 0)
|
||||
{
|
||||
program.RunArguments(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
program.RunInteractive();
|
||||
}
|
||||
|
||||
Console.WriteLine("Program ended");
|
||||
}
|
||||
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
private readonly BlockingCollection<string> _inputQueue;
|
||||
private readonly Thread _consoleThread;
|
||||
private readonly Dictionary<string, IConsoleCommand> _commands;
|
||||
private readonly List<ISwitchConsumer> _parameterConsumers;
|
||||
private readonly ILogger _logger;
|
||||
private readonly LogWriter _logWriter;
|
||||
private readonly SwitchCommand _switchCommand;
|
||||
|
||||
private Program()
|
||||
{
|
||||
_logger = LogProvider.Logger(this);
|
||||
_commands = new Dictionary<string, IConsoleCommand>();
|
||||
_inputQueue = new BlockingCollection<string>();
|
||||
_parameterConsumers = new List<ISwitchConsumer>();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_consoleThread = new Thread(ReadConsoleThread);
|
||||
_logWriter = new LogWriter();
|
||||
_switchCommand = new SwitchCommand(_parameterConsumers);
|
||||
Console.CancelKeyPress += ConsoleOnCancelKeyPress;
|
||||
}
|
||||
|
||||
private void LoadCommands()
|
||||
{
|
||||
AddCommand(new ShowCommand());
|
||||
AddCommand(new ServerCommand(_logWriter));
|
||||
AddCommand(new HelpCommand(_commands));
|
||||
AddCommand(new ExitCommand());
|
||||
AddCommand(new DecryptCommand());
|
||||
AddCommand(_switchCommand);
|
||||
}
|
||||
|
||||
private void LoadGlobalParameterConsumer()
|
||||
{
|
||||
_parameterConsumers.Add(_logWriter);
|
||||
}
|
||||
|
||||
private void RunArguments(string[] arguments)
|
||||
{
|
||||
if (arguments.Length <= 0)
|
||||
{
|
||||
_logger.Error("Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
LoadCommands();
|
||||
LoadGlobalParameterConsumer();
|
||||
ShowCopyright();
|
||||
_logger.Info("Argument Mode");
|
||||
_logger.Info("Press `e'-key to exit.");
|
||||
|
||||
ProcessArguments(arguments);
|
||||
|
||||
ConsoleKeyInfo keyInfo = Console.ReadKey();
|
||||
while (keyInfo.Key != ConsoleKey.E)
|
||||
{
|
||||
keyInfo = Console.ReadKey();
|
||||
}
|
||||
|
||||
ShutdownCommands();
|
||||
}
|
||||
|
||||
private void RunInteractive()
|
||||
{
|
||||
LoadCommands();
|
||||
LoadGlobalParameterConsumer();
|
||||
ShowCopyright();
|
||||
|
||||
_logger.Info("Interactive Mode");
|
||||
|
||||
_consoleThread.IsBackground = true;
|
||||
_consoleThread.Name = "Console Thread";
|
||||
_consoleThread.Start();
|
||||
|
||||
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
string line;
|
||||
try
|
||||
{
|
||||
line = _inputQueue.Take(_cancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
line = null;
|
||||
}
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
// Ctrl+Z, Ctrl+C or error
|
||||
break;
|
||||
}
|
||||
|
||||
string[] arguments = Util.ParseTextArguments(line, CliSeparator, '"');
|
||||
if (arguments.Length <= 0)
|
||||
{
|
||||
_logger.Error($"Invalid input: '{line}'. Type 'help' for a list of available commands.");
|
||||
continue;
|
||||
}
|
||||
|
||||
CommandResultType result = ProcessArguments(arguments);
|
||||
if (result == CommandResultType.Exit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == CommandResultType.Continue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result == CommandResultType.Completed)
|
||||
{
|
||||
_logger.Info("Command Completed");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
StopReadConsoleThread();
|
||||
ShutdownCommands();
|
||||
}
|
||||
|
||||
private CommandResultType ProcessArguments(string[] arguments)
|
||||
{
|
||||
ConsoleParameter parameter = ParseParameter(arguments);
|
||||
|
||||
if (!_commands.ContainsKey(parameter.Key))
|
||||
{
|
||||
_logger.Error(
|
||||
$"Command: '{parameter.Key}' not available. Type `help' for a list of available commands.");
|
||||
return CommandResultType.Continue;
|
||||
}
|
||||
|
||||
IConsoleCommand consoleCommand = _commands[parameter.Key];
|
||||
if (consoleCommand != _switchCommand)
|
||||
{
|
||||
_switchCommand.Handle(parameter);
|
||||
}
|
||||
|
||||
return consoleCommand.Handle(parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the input text into arguments and switches.
|
||||
/// </summary>
|
||||
private ConsoleParameter ParseParameter(string[] args)
|
||||
{
|
||||
if (args.Length <= 0)
|
||||
{
|
||||
_logger.Error("Invalid input. Type 'help' for a list of available commands.");
|
||||
return null;
|
||||
}
|
||||
|
||||
string paramKey = args[0];
|
||||
int cmdLength = args.Length - 1;
|
||||
string[] newArguments = new string[cmdLength];
|
||||
if (cmdLength > 0)
|
||||
{
|
||||
Array.Copy(args, 1, newArguments, 0, cmdLength);
|
||||
}
|
||||
|
||||
args = newArguments;
|
||||
|
||||
ConsoleParameter parameter = new ConsoleParameter(paramKey);
|
||||
foreach (string arg in args)
|
||||
{
|
||||
int count = CountChar(arg, CliValueSeparator);
|
||||
if (count == 1)
|
||||
{
|
||||
string[] keyValue = arg.Split(CliValueSeparator);
|
||||
if (keyValue.Length == 2)
|
||||
{
|
||||
string key = keyValue[0];
|
||||
string value = keyValue[1];
|
||||
if (key.StartsWith('-'))
|
||||
{
|
||||
if (key.Length <= 2 || parameter.SwitchMap.ContainsKey(key))
|
||||
{
|
||||
_logger.Error($"Invalid switch key: '{key}' is empty or duplicated.");
|
||||
continue;
|
||||
}
|
||||
|
||||
parameter.SwitchMap.Add(key, value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.Length <= 0 || parameter.ArgumentMap.ContainsKey(key))
|
||||
{
|
||||
_logger.Error($"Invalid argument key: '{key}' is empty or duplicated.");
|
||||
continue;
|
||||
}
|
||||
|
||||
parameter.ArgumentMap.Add(key, value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg.StartsWith('-'))
|
||||
{
|
||||
string switchStr = arg;
|
||||
if (switchStr.Length <= 2 || parameter.Switches.Contains(switchStr))
|
||||
{
|
||||
_logger.Error($"Invalid switch: '{switchStr}' is empty or duplicated.");
|
||||
continue;
|
||||
}
|
||||
|
||||
parameter.Switches.Add(switchStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.Length <= 0 || parameter.Switches.Contains(arg))
|
||||
{
|
||||
_logger.Error($"Invalid argument: '{arg}' is empty or duplicated.");
|
||||
continue;
|
||||
}
|
||||
|
||||
parameter.Arguments.Add(arg);
|
||||
}
|
||||
|
||||
return parameter;
|
||||
}
|
||||
|
||||
private int CountChar(string str, char chr)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (char c in str)
|
||||
{
|
||||
if (c == chr)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
private void ReadConsoleThread()
|
||||
{
|
||||
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
if (Console.ReadKey().Key != ConsoleKey.Enter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_logWriter.Pause();
|
||||
Console.WriteLine("Enter Command:");
|
||||
string line = Console.ReadLine();
|
||||
try
|
||||
{
|
||||
_inputQueue.Add(line, _cancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
|
||||
_logWriter.Continue();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopReadConsoleThread()
|
||||
{
|
||||
if (_consoleThread != null
|
||||
&& _consoleThread.IsAlive
|
||||
&& Thread.CurrentThread != _consoleThread
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
_consoleThread.Interrupt();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
|
||||
if (!_consoleThread.Join(TimeSpan.FromMilliseconds(500)))
|
||||
{
|
||||
try
|
||||
{
|
||||
_consoleThread.Abort();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConsoleOnCancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
private void AddCommand(IConsoleCommand command)
|
||||
{
|
||||
_commands.Add(command.Key, command);
|
||||
}
|
||||
|
||||
private void ShutdownCommands()
|
||||
{
|
||||
foreach (IConsoleCommand command in _commands.Values)
|
||||
{
|
||||
command.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowCopyright()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("Mhf.Cli Copyright (C) 2019-2020 Mhf Team");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("This is free software, and you are welcome to redistribute it");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append("under certain conditions; type `show c' for details.");
|
||||
sb.Append(Environment.NewLine);
|
||||
sb.Append(Environment.NewLine);
|
||||
_logger.Info(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
19
Mhf.Server/Common/BufferProvider.cs
Normal file
19
Mhf.Server/Common/BufferProvider.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Arrowgene.Services.Buffers;
|
||||
|
||||
namespace Mhf.Server.Common
|
||||
{
|
||||
public class BufferProvider
|
||||
{
|
||||
private static readonly IBufferProvider Provider = new StreamBuffer();
|
||||
|
||||
public static IBuffer Provide()
|
||||
{
|
||||
return Provider.Provide();
|
||||
}
|
||||
|
||||
public static IBuffer Provide(byte[] data)
|
||||
{
|
||||
return Provider.Provide(data);
|
||||
}
|
||||
}
|
||||
}
|
121
Mhf.Server/Common/Crc/Crc32.cs
Normal file
121
Mhf.Server/Common/Crc/Crc32.cs
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright (c) Damien Guard. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Mhf.Server.Common.Crc
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a 32-bit CRC hash algorithm compatible with Zip etc.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Crc32 should only be used for backward compatibility with older file formats
|
||||
/// and algorithms. It is not secure enough for new applications.
|
||||
/// If you need to call multiple times for the same data either use the HashAlgorithm
|
||||
/// interface or remember that the result of one Compute call needs to be ~ (XOR) before
|
||||
/// being passed in as the seed for the next Compute call.
|
||||
/// </remarks>
|
||||
public sealed class Crc32 : HashAlgorithm
|
||||
{
|
||||
public const UInt32 DefaultPolynomial = 0xedb88320u;
|
||||
public const UInt32 DefaultSeed = 0xffffffffu;
|
||||
|
||||
static UInt32[] defaultTable;
|
||||
|
||||
readonly UInt32 seed;
|
||||
readonly UInt32[] table;
|
||||
UInt32 hash;
|
||||
|
||||
public Crc32()
|
||||
: this(DefaultPolynomial, DefaultSeed)
|
||||
{
|
||||
}
|
||||
|
||||
public Crc32(UInt32 polynomial, UInt32 seed)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
throw new PlatformNotSupportedException("Not supported on Big Endian processors");
|
||||
|
||||
table = InitializeTable(polynomial);
|
||||
this.seed = hash = seed;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
hash = seed;
|
||||
}
|
||||
|
||||
protected override void HashCore(byte[] array, int ibStart, int cbSize)
|
||||
{
|
||||
hash = CalculateHash(table, hash, array, ibStart, cbSize);
|
||||
}
|
||||
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
var hashBuffer = UInt32ToBigEndianBytes(~hash);
|
||||
HashValue = hashBuffer;
|
||||
return hashBuffer;
|
||||
}
|
||||
|
||||
public override int HashSize { get { return 32; } }
|
||||
|
||||
public static UInt32 Compute(byte[] buffer)
|
||||
{
|
||||
return Compute(DefaultSeed, buffer);
|
||||
}
|
||||
|
||||
public static UInt32 Compute(UInt32 seed, byte[] buffer)
|
||||
{
|
||||
return Compute(DefaultPolynomial, seed, buffer);
|
||||
}
|
||||
|
||||
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
|
||||
{
|
||||
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
static UInt32[] InitializeTable(UInt32 polynomial)
|
||||
{
|
||||
if (polynomial == DefaultPolynomial && defaultTable != null)
|
||||
return defaultTable;
|
||||
|
||||
var createTable = new UInt32[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var entry = (UInt32)i;
|
||||
for (var j = 0; j < 8; j++)
|
||||
if ((entry & 1) == 1)
|
||||
entry = (entry >> 1) ^ polynomial;
|
||||
else
|
||||
entry = entry >> 1;
|
||||
createTable[i] = entry;
|
||||
}
|
||||
|
||||
if (polynomial == DefaultPolynomial)
|
||||
defaultTable = createTable;
|
||||
|
||||
return createTable;
|
||||
}
|
||||
|
||||
static UInt32 CalculateHash(UInt32[] table, UInt32 seed, IList<byte> buffer, int start, int size)
|
||||
{
|
||||
var hash = seed;
|
||||
for (var i = start; i < start + size; i++)
|
||||
hash = (hash >> 8) ^ table[buffer[i] ^ hash & 0xff];
|
||||
return hash;
|
||||
}
|
||||
|
||||
static byte[] UInt32ToBigEndianBytes(UInt32 uint32)
|
||||
{
|
||||
var result = BitConverter.GetBytes(uint32);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
260
Mhf.Server/Common/CryptoRandom.cs
Normal file
260
Mhf.Server/Common/CryptoRandom.cs
Normal file
@ -0,0 +1,260 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Mhf.Server.Common
|
||||
{
|
||||
/*
|
||||
* Original version by Stephen Toub and Shawn Farkas.
|
||||
* Random pool and thread safety added by Markus Olsson (freakcode.com).
|
||||
*
|
||||
* Original source: http://msdn.microsoft.com/en-us/magazine/cc163367.aspx
|
||||
*
|
||||
* Some benchmarks (2009-03-18):
|
||||
*
|
||||
* Results produced by calling Next() 1 000 000 times on my machine (dual core 3Ghz)
|
||||
*
|
||||
* System.Random completed in 20.4993 ms (avg 0 ms) (first: 0.3454 ms)
|
||||
* CryptoRandom with pool completed in 132.2408 ms (avg 0.0001 ms) (first: 0.025 ms)
|
||||
* CryptoRandom without pool completed in 2 sec 587.708 ms (avg 0.0025 ms) (first: 1.4142 ms)
|
||||
*
|
||||
* |---------------------|------------------------------------|
|
||||
* | Implementation | Slowdown compared to System.Random |
|
||||
* |---------------------|------------------------------------|
|
||||
* | System.Random | 0 |
|
||||
* | CryptoRand w pool | 6,6x |
|
||||
* | CryptoRand w/o pool | 19,5x |
|
||||
* |---------------------|------------------------------------|
|
||||
*
|
||||
* ent (http://www.fourmilab.ch/) results for 16mb of data produced by this class:
|
||||
*
|
||||
* > Entropy = 7.999989 bits per byte.
|
||||
* >
|
||||
* > Optimum compression would reduce the size of this 16777216 byte file by 0 percent.
|
||||
* >
|
||||
* > Chi square distribution for 16777216 samples is 260.64,
|
||||
* > and randomly would exceed this value 50.00 percent of the times.
|
||||
* >
|
||||
* > Arithmetic mean value of data bytes is 127.4974 (127.5 = random).
|
||||
* > Monte Carlo value for Pi is 3.141838823 (error 0.01 percent).
|
||||
* > Serial correlation coefficient is 0.000348 (totally uncorrelated = 0.0).
|
||||
*
|
||||
* your mileage may vary ;)
|
||||
*
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// A random number generator based on the RNGCryptoServiceProvider.
|
||||
/// Adapted from the "Tales from the CryptoRandom" article in MSDN Magazine (September 2007)
|
||||
/// but with explicit guarantee to be thread safe. Note that this implementation also includes
|
||||
/// an optional (enabled by default) random buffer which provides a significant speed boost as
|
||||
/// it greatly reduces the amount of calls into unmanaged land.
|
||||
/// </summary>
|
||||
public class CryptoRandom : Random
|
||||
{
|
||||
private RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
|
||||
|
||||
private byte[] _buffer;
|
||||
|
||||
private int _bufferPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has random pool enabled.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance has random pool enabled; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsRandomPoolEnabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CryptoRandom"/> class with.
|
||||
/// Using this overload will enable the random buffer pool.
|
||||
/// </summary>
|
||||
public CryptoRandom() : this(true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CryptoRandom"/> class.
|
||||
/// This method will disregard whatever value is passed as seed and it's only implemented
|
||||
/// in order to be fully backwards compatible with <see cref="System.Random"/>.
|
||||
/// Using this overload will enable the random buffer pool.
|
||||
/// </summary>
|
||||
/// <param name="ignoredSeed">The ignored seed.</param>
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "ignoredSeed",
|
||||
Justification = "Cannot remove this parameter as we implement the full API of System.Random")]
|
||||
public CryptoRandom(int ignoredSeed) : this(true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CryptoRandom"/> class with
|
||||
/// optional random buffer.
|
||||
/// </summary>
|
||||
/// <param name="enableRandomPool">set to <c>true</c> to enable the random pool buffer for increased performance.</param>
|
||||
public CryptoRandom(bool enableRandomPool)
|
||||
{
|
||||
IsRandomPoolEnabled = enableRandomPool;
|
||||
}
|
||||
|
||||
private void InitBuffer()
|
||||
{
|
||||
if (IsRandomPoolEnabled)
|
||||
{
|
||||
if (_buffer == null || _buffer.Length != 512)
|
||||
_buffer = new byte[512];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_buffer == null || _buffer.Length != 4)
|
||||
_buffer = new byte[4];
|
||||
}
|
||||
|
||||
_rng.GetBytes(_buffer);
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nonnegative random number.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A 32-bit signed integer greater than or equal to zero and less than <see cref="F:System.Int32.MaxValue"/>.
|
||||
/// </returns>
|
||||
public override int Next()
|
||||
{
|
||||
// Mask away the sign bit so that we always return nonnegative integers
|
||||
return (int) GetRandomUInt32() & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nonnegative random number less than the specified maximum.
|
||||
/// </summary>
|
||||
/// <param name="maxValue">The exclusive upper bound of the random number to be generated. <paramref name="maxValue"/> must be greater than or equal to zero.</param>
|
||||
/// <returns>
|
||||
/// A 32-bit signed integer greater than or equal to zero, and less than <paramref name="maxValue"/>; that is, the range of return values ordinarily includes zero but not <paramref name="maxValue"/>. However, if <paramref name="maxValue"/> equals zero, <paramref name="maxValue"/> is returned.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="maxValue"/> is less than zero.
|
||||
/// </exception>
|
||||
public override int Next(int maxValue)
|
||||
{
|
||||
if (maxValue < 0)
|
||||
throw new ArgumentOutOfRangeException("maxValue");
|
||||
|
||||
return Next(0, maxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random number within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="minValue">The inclusive lower bound of the random number returned.</param>
|
||||
/// <param name="maxValue">The exclusive upper bound of the random number returned. <paramref name="maxValue"/> must be greater than or equal to <paramref name="minValue"/>.</param>
|
||||
/// <returns>
|
||||
/// A 32-bit signed integer greater than or equal to <paramref name="minValue"/> and less than <paramref name="maxValue"/>; that is, the range of return values includes <paramref name="minValue"/> but not <paramref name="maxValue"/>. If <paramref name="minValue"/> equals <paramref name="maxValue"/>, <paramref name="minValue"/> is returned.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">
|
||||
/// <paramref name="minValue"/> is greater than <paramref name="maxValue"/>.
|
||||
/// </exception>
|
||||
public override int Next(int minValue, int maxValue)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
throw new ArgumentOutOfRangeException("minValue");
|
||||
|
||||
if (minValue == maxValue)
|
||||
return minValue;
|
||||
|
||||
long diff = maxValue - minValue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint rand = GetRandomUInt32();
|
||||
|
||||
long max = 1 + (long) uint.MaxValue;
|
||||
long remainder = max % diff;
|
||||
|
||||
if (rand < max - remainder)
|
||||
return (int) (minValue + (rand % diff));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random number between 0.0 and 1.0.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
|
||||
/// </returns>
|
||||
public override double NextDouble()
|
||||
{
|
||||
return GetRandomUInt32() / (1.0 + uint.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the elements of a specified array of bytes with random numbers.
|
||||
/// </summary>
|
||||
/// <param name="buffer">An array of bytes to contain random numbers.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">
|
||||
/// <paramref name="buffer"/> is null.
|
||||
/// </exception>
|
||||
public override void NextBytes(byte[] buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (IsRandomPoolEnabled && _buffer == null)
|
||||
InitBuffer();
|
||||
|
||||
// Can we fit the requested number of bytes in the buffer?
|
||||
if (IsRandomPoolEnabled && _buffer.Length <= buffer.Length)
|
||||
{
|
||||
int count = buffer.Length;
|
||||
|
||||
EnsureRandomBuffer(count);
|
||||
|
||||
Buffer.BlockCopy(_buffer, _bufferPosition, buffer, 0, count);
|
||||
|
||||
_bufferPosition += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw bytes directly from the RNGCryptoProvider
|
||||
_rng.GetBytes(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets one random unsigned 32bit integer in a thread safe manner.
|
||||
/// </summary>
|
||||
private uint GetRandomUInt32()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
EnsureRandomBuffer(4);
|
||||
|
||||
uint rand = BitConverter.ToUInt32(_buffer, _bufferPosition);
|
||||
|
||||
_bufferPosition += 4;
|
||||
|
||||
return rand;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that we have enough bytes in the random buffer.
|
||||
/// </summary>
|
||||
/// <param name="requiredBytes">The number of required bytes.</param>
|
||||
private void EnsureRandomBuffer(int requiredBytes)
|
||||
{
|
||||
if (_buffer == null)
|
||||
InitBuffer();
|
||||
|
||||
if (requiredBytes > _buffer.Length)
|
||||
throw new ArgumentOutOfRangeException("requiredBytes", "cannot be greater than random buffer");
|
||||
|
||||
if ((_buffer.Length - _bufferPosition) < requiredBytes)
|
||||
InitBuffer();
|
||||
}
|
||||
}
|
||||
}
|
7
Mhf.Server/Common/Instance/IInstance.cs
Normal file
7
Mhf.Server/Common/Instance/IInstance.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Mhf.Server.Common.Instance
|
||||
{
|
||||
public interface IInstance
|
||||
{
|
||||
uint InstanceId { get; set; }
|
||||
}
|
||||
}
|
60
Mhf.Server/Common/Instance/InstanceGenerator.cs
Normal file
60
Mhf.Server/Common/Instance/InstanceGenerator.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mhf.Server.Common.Instance
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides Unique Ids for instancing.
|
||||
/// </summary>
|
||||
public class InstanceGenerator
|
||||
{
|
||||
private readonly object _lock;
|
||||
private uint _currentId;
|
||||
|
||||
private readonly Dictionary<uint, IInstance> _instances;
|
||||
|
||||
public InstanceGenerator()
|
||||
{
|
||||
_lock = new object();
|
||||
_currentId = 0;
|
||||
_instances = new Dictionary<uint, IInstance>();
|
||||
}
|
||||
|
||||
public void AssignInstance(IInstance instance)
|
||||
{
|
||||
uint id;
|
||||
lock (_lock)
|
||||
{
|
||||
id = _currentId;
|
||||
_currentId++;
|
||||
}
|
||||
|
||||
_instances.Add(id, instance);
|
||||
instance.InstanceId = id;
|
||||
}
|
||||
|
||||
public T CreateInstance<T>() where T : IInstance, new()
|
||||
{
|
||||
uint id;
|
||||
lock (_lock)
|
||||
{
|
||||
id = _currentId;
|
||||
_currentId++;
|
||||
}
|
||||
|
||||
T instance = new T();
|
||||
_instances.Add(id, instance);
|
||||
instance.InstanceId = id;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public IInstance GetInstance(uint id)
|
||||
{
|
||||
if (!_instances.ContainsKey(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _instances[id];
|
||||
}
|
||||
}
|
||||
}
|
77
Mhf.Server/Common/MhfUpDat.cs
Normal file
77
Mhf.Server/Common/MhfUpDat.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Mhf.Server.Common.Crc;
|
||||
|
||||
namespace Mhf.Server.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates MHFUP.DAT file of all files from specified root directory.
|
||||
/// The file has entry ended by a LineFeed (LF / 0x0A)
|
||||
/// </summary>
|
||||
public class MhfUpDat
|
||||
{
|
||||
private readonly DirectoryInfo _rootDirectoryInfo;
|
||||
|
||||
public MhfUpDat(string rootDirectoryPath)
|
||||
{
|
||||
_rootDirectoryInfo = new DirectoryInfo(rootDirectoryPath);
|
||||
if (!_rootDirectoryInfo.Exists)
|
||||
{
|
||||
throw new FileNotFoundException("Folder does not exist", rootDirectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetUpdateEntry(string filePath)
|
||||
{
|
||||
return GetUpdateEntry(new FileInfo(filePath));
|
||||
}
|
||||
|
||||
public string GetUpdateEntry(FileInfo fileInfo)
|
||||
{
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fileInfo.Refresh();
|
||||
byte[] file = Util.ReadFile(fileInfo.FullName);
|
||||
uint crc32 = Crc32.Compute(file);
|
||||
DateTime lastWriteTime = fileInfo.LastWriteTime;
|
||||
long lastWriteFileTime = lastWriteTime.ToFileTime();
|
||||
string lastWriteFileTimeHex = $"{lastWriteFileTime:X16}";
|
||||
string fileTimeHex1 = lastWriteFileTimeHex.Substring(8);
|
||||
string fileTimeHex2 = lastWriteFileTimeHex.Substring(0, 8);
|
||||
return $"{crc32:X8},{fileTimeHex1},{fileTimeHex2},{GetFileNameEntry(fileInfo)},{file.Length},0";
|
||||
}
|
||||
|
||||
public string CreateMhfUpDat()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (FileInfo fileInfo in _rootDirectoryInfo.GetFiles("*", SearchOption.AllDirectories))
|
||||
{
|
||||
sb.Append($"{GetUpdateEntry(fileInfo)}/n");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void SaveMhfUpDat(string destinationFilePath)
|
||||
{
|
||||
string mhfUpDat = CreateMhfUpDat();
|
||||
Util.WriteFile(Encoding.UTF8.GetBytes(mhfUpDat), destinationFilePath);
|
||||
}
|
||||
|
||||
private string GetFileNameEntry(FileInfo fileInfo)
|
||||
{
|
||||
string relativeDirectory = Util.RelativeDirectory(_rootDirectoryInfo.FullName, fileInfo.DirectoryName);
|
||||
if (string.IsNullOrEmpty(relativeDirectory))
|
||||
{
|
||||
return $"exe\\{fileInfo.Name}";
|
||||
}
|
||||
|
||||
string path = Path.Combine(relativeDirectory, fileInfo.Name);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
7
Mhf.Server/Common/Middleware/IMiddleware.cs
Normal file
7
Mhf.Server/Common/Middleware/IMiddleware.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Mhf.Server.Common.Middleware
|
||||
{
|
||||
public interface IMiddleware<T, TReq, TRes>
|
||||
{
|
||||
void Handle(T user, TReq request, TRes response, MiddlewareDelegate<T, TReq, TRes> next);
|
||||
}
|
||||
}
|
16
Mhf.Server/Common/Middleware/Middleware.cs
Normal file
16
Mhf.Server/Common/Middleware/Middleware.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Arrowgene.Services.Logging;
|
||||
|
||||
namespace Mhf.Server.Common.Middleware
|
||||
{
|
||||
public abstract class Middleware<T, TReq, TRes> : IMiddleware<T, TReq, TRes>
|
||||
{
|
||||
protected Middleware()
|
||||
{
|
||||
Logger = LogProvider.Logger(this);
|
||||
}
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
public abstract void Handle(T client, TReq message, TRes response, MiddlewareDelegate<T, TReq, TRes> next);
|
||||
}
|
||||
}
|
4
Mhf.Server/Common/Middleware/MiddlewareDelegate.cs
Normal file
4
Mhf.Server/Common/Middleware/MiddlewareDelegate.cs
Normal file
@ -0,0 +1,4 @@
|
||||
namespace Mhf.Server.Common.Middleware
|
||||
{
|
||||
public delegate void MiddlewareDelegate<T, TReq, TRes>(T user, TReq request, TRes response);
|
||||
}
|
34
Mhf.Server/Common/Middleware/MiddlewareStack.cs
Normal file
34
Mhf.Server/Common/Middleware/MiddlewareStack.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace Mhf.Server.Common.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of a middleware
|
||||
/// </summary>
|
||||
public class MiddlewareStack<T, TReq, TRes>
|
||||
{
|
||||
private MiddlewareDelegate<T, TReq, TRes> _middlewareDelegate;
|
||||
|
||||
public MiddlewareStack(MiddlewareDelegate<T, TReq, TRes> kernel)
|
||||
{
|
||||
_middlewareDelegate = kernel;
|
||||
}
|
||||
|
||||
public void Start(T user, TReq request, TRes response)
|
||||
{
|
||||
_middlewareDelegate(user, request, response);
|
||||
}
|
||||
|
||||
public MiddlewareStack<T, TReq, TRes> Use(
|
||||
Func<MiddlewareDelegate<T, TReq, TRes>, MiddlewareDelegate<T, TReq, TRes>> middleware)
|
||||
{
|
||||
_middlewareDelegate = middleware(_middlewareDelegate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MiddlewareStack<T, TReq, TRes> Use(IMiddleware<T, TReq, TRes> middleware)
|
||||
{
|
||||
return Use(next => (user, request, response) => middleware.Handle(user, request, response, next));
|
||||
}
|
||||
}
|
||||
}
|
695
Mhf.Server/Common/Util.cs
Normal file
695
Mhf.Server/Common/Util.cs
Normal file
@ -0,0 +1,695 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Common
|
||||
{
|
||||
public class Util
|
||||
{
|
||||
public static readonly CryptoRandom Random = new CryptoRandom();
|
||||
|
||||
private static readonly Random RandomNum = new Random();
|
||||
|
||||
public static int GetRandomNumber(int min, int max)
|
||||
{
|
||||
lock (RandomNum)
|
||||
{
|
||||
return RandomNum.Next(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
public static long GetUnixTime(DateTime dateTime)
|
||||
{
|
||||
return ((DateTimeOffset) dateTime).ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
public static string PathDifferenceEnd(string directoryInfo1, string directoryInfo2, bool unRoot)
|
||||
{
|
||||
return PathDifference(new DirectoryInfo(directoryInfo1), new DirectoryInfo(directoryInfo2), unRoot);
|
||||
}
|
||||
|
||||
public static string PathDifferenceEnd(FileSystemInfo directoryInfo1, FileSystemInfo directoryInfo2,
|
||||
bool unRoot)
|
||||
{
|
||||
string result;
|
||||
if (directoryInfo1.FullName == directoryInfo2.FullName)
|
||||
{
|
||||
result = directoryInfo1.FullName;
|
||||
}
|
||||
else if (directoryInfo1.FullName.EndsWith(directoryInfo2.FullName))
|
||||
{
|
||||
result = directoryInfo1.FullName.Split(new[] {directoryInfo2.FullName},
|
||||
StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
}
|
||||
else if (directoryInfo2.FullName.EndsWith(directoryInfo1.FullName))
|
||||
{
|
||||
result = directoryInfo2.FullName.Split(new[] {directoryInfo1.FullName},
|
||||
StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "";
|
||||
}
|
||||
|
||||
if (unRoot)
|
||||
{
|
||||
result = UnRootPath(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static string PathDifference(string directoryInfo1, string directoryInfo2, bool unRoot)
|
||||
{
|
||||
return PathDifference(new DirectoryInfo(directoryInfo1), new DirectoryInfo(directoryInfo2), unRoot);
|
||||
}
|
||||
|
||||
public static string PathDifference(FileSystemInfo directoryInfo1, FileSystemInfo directoryInfo2, bool unRoot)
|
||||
{
|
||||
string result;
|
||||
if (directoryInfo1.FullName == directoryInfo2.FullName)
|
||||
{
|
||||
result = "";
|
||||
}
|
||||
else if (directoryInfo1.FullName.StartsWith(directoryInfo2.FullName))
|
||||
{
|
||||
result = directoryInfo1.FullName.Split(new[] {directoryInfo2.FullName},
|
||||
StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
}
|
||||
else if (directoryInfo2.FullName.StartsWith(directoryInfo1.FullName))
|
||||
{
|
||||
result = directoryInfo2.FullName.Split(new[] {directoryInfo1.FullName},
|
||||
StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "";
|
||||
}
|
||||
|
||||
if (unRoot)
|
||||
{
|
||||
result = UnRootPath(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string UnRootPath(string path)
|
||||
{
|
||||
// https://stackoverflow.com/questions/53102/why-does-path-combine-not-properly-concatenate-filenames-that-start-with-path-di
|
||||
if (Path.IsPathRooted(path))
|
||||
{
|
||||
path = path.TrimStart(Path.DirectorySeparatorChar);
|
||||
path = path.TrimStart(Path.AltDirectorySeparatorChar);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static AssemblyName GetAssemblyName(string name)
|
||||
{
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
AssemblyName assemblyName = assembly.GetName();
|
||||
if (assemblyName.Name == name)
|
||||
{
|
||||
return assemblyName;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Version GetAssemblyVersion(string name)
|
||||
{
|
||||
AssemblyName assemblyName = GetAssemblyName(name);
|
||||
if (assemblyName != null)
|
||||
{
|
||||
return assemblyName.Version;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetAssemblyVersionString(string name)
|
||||
{
|
||||
Version version = GetAssemblyVersion(name);
|
||||
if (version != null)
|
||||
{
|
||||
return version.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] ReadFile(string source)
|
||||
{
|
||||
if (!File.Exists(source))
|
||||
{
|
||||
throw new Exception(string.Format("'{0}' does not exist or is not a file", source));
|
||||
}
|
||||
|
||||
return File.ReadAllBytes(source);
|
||||
}
|
||||
|
||||
public static string ReadFileText(string source)
|
||||
{
|
||||
if (!File.Exists(source))
|
||||
{
|
||||
throw new Exception($"'{source}' does not exist or is not a file");
|
||||
}
|
||||
|
||||
return File.ReadAllText(source);
|
||||
}
|
||||
|
||||
public static void WriteFile(byte[] content, string destination)
|
||||
{
|
||||
if (content != null)
|
||||
{
|
||||
File.WriteAllBytes(destination, content);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Content of '{destination}' is null");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<FileInfo> GetFiles(DirectoryInfo directoryInfo, string[] extensions, bool recursive)
|
||||
{
|
||||
if (recursive)
|
||||
{
|
||||
List<FileInfo> filteredFiles = GetFiles(directoryInfo, extensions);
|
||||
DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories("*", SearchOption.TopDirectoryOnly);
|
||||
foreach (DirectoryInfo dInfo in directoryInfos)
|
||||
{
|
||||
List<FileInfo> files = GetFiles(dInfo, extensions, true);
|
||||
filteredFiles.AddRange(files);
|
||||
}
|
||||
|
||||
return filteredFiles;
|
||||
}
|
||||
|
||||
return GetFiles(directoryInfo, extensions);
|
||||
}
|
||||
|
||||
public static List<FileInfo> GetFiles(DirectoryInfo directoryInfo, string[] extensions)
|
||||
{
|
||||
List<FileInfo> filteredFiles = new List<FileInfo>();
|
||||
FileInfo[] files = directoryInfo.GetFiles("*.*", SearchOption.TopDirectoryOnly);
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
if (extensions != null)
|
||||
{
|
||||
foreach (string extension in extensions)
|
||||
{
|
||||
if (file.Extension.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filteredFiles.Add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredFiles.Add(file);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredFiles;
|
||||
}
|
||||
|
||||
public static List<DirectoryInfo> GetFolders(DirectoryInfo directoryInfo, string[] extensions, bool recursive)
|
||||
{
|
||||
if (recursive)
|
||||
{
|
||||
List<DirectoryInfo> result = new List<DirectoryInfo>();
|
||||
List<DirectoryInfo> filteredDirectories = GetFolders(directoryInfo, extensions);
|
||||
result.AddRange(filteredDirectories);
|
||||
foreach (DirectoryInfo directory in filteredDirectories)
|
||||
{
|
||||
List<DirectoryInfo> directories = GetFolders(directory, extensions, true);
|
||||
result.AddRange(directories);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return GetFolders(directoryInfo, extensions);
|
||||
}
|
||||
|
||||
public static List<DirectoryInfo> GetFolders(DirectoryInfo directoryInfo, string[] extensions)
|
||||
{
|
||||
List<DirectoryInfo> filteredDirectories = new List<DirectoryInfo>();
|
||||
DirectoryInfo[] directories = directoryInfo.GetDirectories("*", SearchOption.TopDirectoryOnly);
|
||||
foreach (DirectoryInfo directory in directories)
|
||||
{
|
||||
if (extensions != null)
|
||||
{
|
||||
foreach (string extension in extensions)
|
||||
{
|
||||
if (directory.Name.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filteredDirectories.Add(directory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredDirectories.Add(directory);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredDirectories;
|
||||
}
|
||||
|
||||
|
||||
public static DirectoryInfo EnsureDirectory(string directory)
|
||||
{
|
||||
return Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The directory of the executing assembly.
|
||||
/// This might not be the location where the .dll files are located.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string ExecutingDirectory()
|
||||
{
|
||||
string path = Assembly.GetEntryAssembly().CodeBase;
|
||||
Uri uri = new Uri(path);
|
||||
string directory = Path.GetDirectoryName(uri.LocalPath);
|
||||
return directory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative directory of the executing assembly.
|
||||
/// This might not be the location where the .dll files are located.
|
||||
/// </summary>
|
||||
public static string RelativeExecutingDirectory()
|
||||
{
|
||||
return RelativeDirectory(Environment.CurrentDirectory, ExecutingDirectory());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Directory of Common.dll
|
||||
/// This is expected to contain ressource files.
|
||||
/// </summary>
|
||||
public static string CommonDirectory()
|
||||
{
|
||||
string location = typeof(Util).GetTypeInfo().Assembly.Location;
|
||||
Uri uri = new Uri(location);
|
||||
string directory = Path.GetDirectoryName(uri.LocalPath);
|
||||
return directory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relative Directory of Common.dll.
|
||||
/// This is expected to contain ressource files.
|
||||
/// </summary>
|
||||
public static string RelativeCommonDirectory()
|
||||
{
|
||||
return RelativeDirectory(Environment.CurrentDirectory, CommonDirectory());
|
||||
}
|
||||
|
||||
public static string CreateMd5(string input)
|
||||
{
|
||||
MD5 md5 = MD5.Create();
|
||||
byte[] inputBytes = Encoding.ASCII.GetBytes(input);
|
||||
byte[] hashBytes = md5.ComputeHash(inputBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < hashBytes.Length; i++)
|
||||
{
|
||||
sb.Append(hashBytes[i].ToString("X2"));
|
||||
}
|
||||
|
||||
return sb.ToString().ToLower();
|
||||
}
|
||||
|
||||
public static string RelativeDirectory(string fromDirectory, string toDirectory)
|
||||
{
|
||||
return RelativeDirectory(fromDirectory, toDirectory, toDirectory, Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
public static string RelativeDirectory(string fromDirectory, string toDirectory, string defaultDirectory)
|
||||
{
|
||||
return RelativeDirectory(fromDirectory, toDirectory, defaultDirectory, Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a directory that is relative.
|
||||
/// </summary>
|
||||
/// <param name="fromDirectory">The directory to navigate from.</param>
|
||||
/// <param name="toDirectory">The directory to reach.</param>
|
||||
/// <param name="defaultDirectory">A directory to return on failure.</param>
|
||||
/// <param name="directorySeparator"></param>
|
||||
/// <returns>The relative directory or the defaultDirectory on failure.</returns>
|
||||
public static string RelativeDirectory(string fromDirectory, string toDirectory, string defaultDirectory,
|
||||
char directorySeparator)
|
||||
{
|
||||
string result;
|
||||
|
||||
if (fromDirectory.EndsWith("\\") || fromDirectory.EndsWith("/"))
|
||||
{
|
||||
fromDirectory = fromDirectory.Remove(fromDirectory.Length - 1);
|
||||
}
|
||||
|
||||
if (toDirectory.EndsWith("\\") || toDirectory.EndsWith("/"))
|
||||
{
|
||||
toDirectory = toDirectory.Remove(toDirectory.Length - 1);
|
||||
}
|
||||
|
||||
if (toDirectory.StartsWith(fromDirectory))
|
||||
{
|
||||
result = toDirectory.Substring(fromDirectory.Length);
|
||||
if (result.StartsWith("\\") || result.StartsWith("/"))
|
||||
{
|
||||
result = result.Substring(1, result.Length - 1);
|
||||
}
|
||||
|
||||
if (result != "")
|
||||
{
|
||||
result += directorySeparator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] fromDirs = fromDirectory.Split(':', '\\', '/');
|
||||
string[] toDirs = toDirectory.Split(':', '\\', '/');
|
||||
if (fromDirs.Length <= 0 || toDirs.Length <= 0 || fromDirs[0] != toDirs[0])
|
||||
{
|
||||
return defaultDirectory;
|
||||
}
|
||||
|
||||
int offset = 1;
|
||||
for (; offset < fromDirs.Length; offset++)
|
||||
{
|
||||
if (toDirs.Length <= offset)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (fromDirs[offset] != toDirs[offset])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder relativeBuilder = new StringBuilder();
|
||||
for (int i = 0; i < fromDirs.Length - offset; i++)
|
||||
{
|
||||
relativeBuilder.Append("..");
|
||||
relativeBuilder.Append(directorySeparator);
|
||||
}
|
||||
|
||||
for (int i = offset; i < toDirs.Length - 1; i++)
|
||||
{
|
||||
relativeBuilder.Append(toDirs[i]);
|
||||
relativeBuilder.Append(directorySeparator);
|
||||
}
|
||||
|
||||
result = relativeBuilder.ToString();
|
||||
}
|
||||
|
||||
result = DirectorySeparator(result, directorySeparator);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string DirectorySeparator(string path)
|
||||
{
|
||||
return DirectorySeparator(path, Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
public static string DirectorySeparator(string path, char directorySeparator)
|
||||
{
|
||||
if (directorySeparator != '\\')
|
||||
{
|
||||
path = path.Replace('\\', directorySeparator);
|
||||
}
|
||||
|
||||
if (directorySeparator != '/')
|
||||
{
|
||||
path = path.Replace('/', directorySeparator);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string GenerateSessionKey(int desiredLength)
|
||||
{
|
||||
StringBuilder sessionKey = new StringBuilder();
|
||||
using (RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider())
|
||||
{
|
||||
byte[] random = new byte[1];
|
||||
int length = 0;
|
||||
while (length < desiredLength)
|
||||
{
|
||||
cryptoProvider.GetBytes(random);
|
||||
char c = (char) random[0];
|
||||
if ((Char.IsDigit(c) || Char.IsLetter(c)) && random[0] < 127)
|
||||
{
|
||||
length++;
|
||||
sessionKey.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sessionKey.ToString();
|
||||
}
|
||||
|
||||
public static byte[] GenerateKey(int desiredLength)
|
||||
{
|
||||
byte[] random = new byte[desiredLength];
|
||||
using (RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider())
|
||||
{
|
||||
cryptoProvider.GetNonZeroBytes(random);
|
||||
}
|
||||
|
||||
return random;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes entries from a collection.
|
||||
/// The input lists are not modified, instead a new collection is returned.
|
||||
/// </summary>
|
||||
public static TList SubtractList<TList, TItem>(TList entries, params TItem[] excepts)
|
||||
where TList : ICollection<TItem>, new()
|
||||
{
|
||||
TList result = new TList();
|
||||
foreach (TItem entry in entries)
|
||||
{
|
||||
result.Add(entry);
|
||||
}
|
||||
|
||||
foreach (TItem except in excepts)
|
||||
{
|
||||
result.Remove(except);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] FromHexString(string hexString)
|
||||
{
|
||||
if ((hexString.Length & 1) != 0)
|
||||
{
|
||||
throw new ArgumentException("Input must have even number of characters");
|
||||
}
|
||||
byte[] ret = new byte[hexString.Length/2];
|
||||
for (int i = 0; i < ret.Length; i++)
|
||||
{
|
||||
int high = hexString[i*2];
|
||||
int low = hexString[i*2+1];
|
||||
high = (high & 0xf) + ((high & 0x40) >> 6) * 9;
|
||||
low = (low & 0xf) + ((low & 0x40) >> 6) * 9;
|
||||
|
||||
ret[i] = (byte)((high << 4) | low);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static string ToHexString(byte[] data, char? seperator = null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int len = data.Length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
sb.Append(data[i].ToString("X2"));
|
||||
if (seperator != null && i < len - 1)
|
||||
{
|
||||
sb.Append(seperator);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string ToAsciiString(byte[] data, bool spaced)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
char c = '.';
|
||||
if (data[i] >= 'A' && data[i] <= 'Z') c = (char) data[i];
|
||||
if (data[i] >= 'a' && data[i] <= 'z') c = (char) data[i];
|
||||
if (data[i] >= '0' && data[i] <= '9') c = (char) data[i];
|
||||
if (spaced && i != 0)
|
||||
{
|
||||
sb.Append(" ");
|
||||
}
|
||||
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string[] ParseTextArguments(string line, char delimiter, char textQualifier)
|
||||
{
|
||||
IList<string> list = ParseTextList(line, delimiter, textQualifier);
|
||||
int count = list.Count;
|
||||
string[] arguments = new string[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
arguments[i] = list[i];
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ParseTextEnumerable(string line, char delimiter, char textQualifier)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
yield break;
|
||||
|
||||
char prevChar = '\0';
|
||||
char nextChar = '\0';
|
||||
char currentChar = '\0';
|
||||
bool inString = false;
|
||||
|
||||
StringBuilder token = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < line.Length; i++)
|
||||
{
|
||||
currentChar = line[i];
|
||||
|
||||
if (i > 0)
|
||||
prevChar = line[i - 1];
|
||||
else
|
||||
prevChar = '\0';
|
||||
|
||||
if (i + 1 < line.Length)
|
||||
nextChar = line[i + 1];
|
||||
else
|
||||
nextChar = '\0';
|
||||
|
||||
if (currentChar == textQualifier && (prevChar == '\0' || prevChar == delimiter) && !inString)
|
||||
{
|
||||
inString = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentChar == textQualifier && (nextChar == '\0' || nextChar == delimiter) && inString)
|
||||
{
|
||||
inString = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentChar == delimiter && !inString)
|
||||
{
|
||||
yield return token.ToString();
|
||||
token = token.Remove(0, token.Length);
|
||||
continue;
|
||||
}
|
||||
|
||||
token = token.Append(currentChar);
|
||||
yield return token.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static IList<string> ParseTextList(string line, char delimiter, char textQualifier)
|
||||
{
|
||||
IList<string> collection = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
return collection;
|
||||
|
||||
char prevChar = '\0';
|
||||
char nextChar = '\0';
|
||||
char currentChar = '\0';
|
||||
bool inString = false;
|
||||
|
||||
StringBuilder token = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < line.Length; i++)
|
||||
{
|
||||
currentChar = line[i];
|
||||
|
||||
if (i > 0)
|
||||
prevChar = line[i - 1];
|
||||
else
|
||||
prevChar = '\0';
|
||||
|
||||
if (i + 1 < line.Length)
|
||||
nextChar = line[i + 1];
|
||||
else
|
||||
nextChar = '\0';
|
||||
|
||||
if (currentChar == textQualifier && (prevChar == '\0' || prevChar == delimiter) && !inString)
|
||||
{
|
||||
inString = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentChar == textQualifier && (nextChar == '\0' || nextChar == delimiter) && inString)
|
||||
{
|
||||
inString = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentChar == delimiter && !inString)
|
||||
{
|
||||
collection.Add(token.ToString());
|
||||
token = token.Remove(0, token.Length);
|
||||
continue;
|
||||
}
|
||||
|
||||
token = token.Append(currentChar);
|
||||
}
|
||||
|
||||
collection.Add(token.ToString());
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a stream till the end and return the read bytes.
|
||||
/// </summary>
|
||||
public static async Task<byte[]> ReadAsync(Stream stream)
|
||||
{
|
||||
int bufferSize = 1024;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
byte[] result = new byte[0];
|
||||
int offset = 0;
|
||||
int read = 0;
|
||||
while ((read = await stream.ReadAsync(buffer, 0, bufferSize)) > 0)
|
||||
{
|
||||
int newSize = offset + read;
|
||||
byte[] temp = new byte[newSize];
|
||||
Buffer.BlockCopy(result, 0, temp, 0, offset);
|
||||
Buffer.BlockCopy(buffer, 0, temp, offset, read);
|
||||
result = temp;
|
||||
offset += read;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
22
Mhf.Server/Database/IDatabase.cs
Normal file
22
Mhf.Server/Database/IDatabase.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Database
|
||||
{
|
||||
public interface IDatabase
|
||||
{
|
||||
void Execute(string sql);
|
||||
|
||||
/// <summary>
|
||||
/// Return true if database was created, or false if not.
|
||||
/// </summary>
|
||||
bool CreateDatabase();
|
||||
|
||||
// Account
|
||||
Account CreateAccount(string name, string mail, string hash);
|
||||
Account SelectAccountById(int accountId);
|
||||
Account SelectAccountByName(string accountName);
|
||||
bool UpdateAccount(Account account);
|
||||
bool DeleteAccount(int accountId);
|
||||
}
|
||||
}
|
52
Mhf.Server/Database/MhfDatabaseBuilder.cs
Normal file
52
Mhf.Server/Database/MhfDatabaseBuilder.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Database.Sql;
|
||||
using Mhf.Server.Model;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Server.Database
|
||||
{
|
||||
public class MhfDatabaseBuilder
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MhfDatabaseBuilder()
|
||||
{
|
||||
_logger = LogProvider.Logger(this);
|
||||
}
|
||||
|
||||
public IDatabase Build(DatabaseSetting settings)
|
||||
{
|
||||
IDatabase database = null;
|
||||
switch (settings.Type)
|
||||
{
|
||||
case DatabaseType.SQLite:
|
||||
database = PrepareSqlLiteDb(settings.SqLiteFolder);
|
||||
break;
|
||||
}
|
||||
|
||||
if (database == null)
|
||||
{
|
||||
_logger.Error("Database could not be created, exiting...");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
private MhfSqLiteDb PrepareSqlLiteDb(string sqLiteFolder)
|
||||
{
|
||||
string sqLitePath = Path.Combine(sqLiteFolder, $"db.v{MhfSqLiteDb.Version}.sqlite");
|
||||
MhfSqLiteDb db = new MhfSqLiteDb(sqLitePath);
|
||||
if (db.CreateDatabase())
|
||||
{
|
||||
ScriptRunner scriptRunner = new ScriptRunner(db);
|
||||
scriptRunner.Run(Path.Combine(sqLiteFolder, "Script/schema_sqlite.sql"));
|
||||
scriptRunner.Run(Path.Combine(sqLiteFolder, "Script/data_account.sql"));
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
}
|
||||
}
|
29
Mhf.Server/Database/Sql/Core/MhfSqlDb.cs
Normal file
29
Mhf.Server/Database/Sql/Core/MhfSqlDb.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Logging;
|
||||
|
||||
namespace Mhf.Server.Database.Sql.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of Mhf database operations.
|
||||
/// </summary>
|
||||
public abstract partial class MhfSqlDb<TCon, TCom> : SqlDb<TCon, TCom>
|
||||
where TCon : DbConnection
|
||||
where TCom : DbCommand
|
||||
{
|
||||
protected readonly MhfLogger Logger;
|
||||
|
||||
|
||||
public MhfSqlDb()
|
||||
{
|
||||
Logger = LogProvider.Logger<MhfLogger>(this);
|
||||
}
|
||||
|
||||
protected override void Exception(Exception ex)
|
||||
{
|
||||
Logger.Exception(ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
131
Mhf.Server/Database/Sql/Core/MhfSqlDbAccount.cs
Normal file
131
Mhf.Server/Database/Sql/Core/MhfSqlDbAccount.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Database.Sql.Core
|
||||
{
|
||||
public abstract partial class MhfSqlDb<TCon, TCom> : SqlDb<TCon, TCom>
|
||||
where TCon : DbConnection
|
||||
where TCom : DbCommand
|
||||
{
|
||||
private const string SqlInsertAccount =
|
||||
"INSERT INTO `account` (`name`, `normal_name`, `hash`, `mail`, `mail_verified`, `mail_verified_at`, `mail_token`, `password_token`, `state`, `last_login`, `created`) VALUES (@name, @normal_name, @hash, @mail, @mail_verified, @mail_verified_at, @mail_token, @password_token, @state, @last_login, @created);";
|
||||
|
||||
private const string SqlSelectAccountById =
|
||||
"SELECT `id`, `name`, `normal_name`, `hash`, `mail`, `mail_verified`, `mail_verified_at`, `mail_token`, `password_token`, `state`, `last_login`, `created` FROM `account` WHERE `id`=@id;";
|
||||
|
||||
private const string SqlSelectAccountByName =
|
||||
"SELECT `id`, `name`, `normal_name`, `hash`, `mail`, `mail_verified`, `mail_verified_at`, `mail_token`, `password_token`, `state`, `last_login`, `created` FROM `account` WHERE `name`=@name;";
|
||||
|
||||
private const string SqlUpdateAccount =
|
||||
"UPDATE `account` SET `name`=@name, `normal_name`=@normal_name, `hash`=@hash, `mail`=@mail, `mail_verified`=@mail_verified, `mail_verified_at`=@mail_verified_at, `mail_token`=@mail_token, `password_token`=@password_token, `state`=@state, `last_login`=@last_login, `created`=@created WHERE `id`=@id;";
|
||||
|
||||
private const string SqlDeleteAccount =
|
||||
"DELETE FROM `account` WHERE `id`=@id;";
|
||||
|
||||
public Account CreateAccount(string name, string mail, string hash)
|
||||
{
|
||||
Account account = new Account();
|
||||
account.Name = name;
|
||||
account.NormalName = name.ToLowerInvariant();
|
||||
account.Mail = mail;
|
||||
account.Hash = hash;
|
||||
account.State = AccountStateType.User;
|
||||
account.Created = DateTime.Now;
|
||||
int rowsAffected = ExecuteNonQuery(SqlInsertAccount, command =>
|
||||
{
|
||||
AddParameter(command, "@name", account.Name);
|
||||
AddParameter(command, "@normal_name", account.NormalName);
|
||||
AddParameter(command, "@hash", account.Hash);
|
||||
AddParameter(command, "@mail", account.Mail);
|
||||
AddParameter(command, "@mail_verified", account.MailVerified);
|
||||
AddParameter(command, "@mail_verified_at", account.MailVerifiedAt);
|
||||
AddParameter(command, "@mail_token", account.MailToken);
|
||||
AddParameter(command, "@password_token", account.PasswordToken);
|
||||
AddParameterEnumInt32(command, "@state", account.State);
|
||||
AddParameter(command, "@last_login", account.LastLogin);
|
||||
AddParameter(command, "@created", account.Created);
|
||||
}, out long autoIncrement);
|
||||
if (rowsAffected <= NoRowsAffected || autoIncrement <= NoAutoIncrement)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
account.Id = (int) autoIncrement;
|
||||
return account;
|
||||
}
|
||||
|
||||
public Account SelectAccountByName(string accountName)
|
||||
{
|
||||
Account account = null;
|
||||
ExecuteReader(SqlSelectAccountByName,
|
||||
command => { AddParameter(command, "@name", accountName); }, reader =>
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
account = ReadAccount(reader);
|
||||
}
|
||||
});
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
public Account SelectAccountById(int accountId)
|
||||
{
|
||||
Account account = null;
|
||||
ExecuteReader(SqlSelectAccountById, command => { AddParameter(command, "@id", accountId); }, reader =>
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
account = ReadAccount(reader);
|
||||
}
|
||||
});
|
||||
return account;
|
||||
}
|
||||
|
||||
public bool UpdateAccount(Account account)
|
||||
{
|
||||
int rowsAffected = ExecuteNonQuery(SqlUpdateAccount, command =>
|
||||
{
|
||||
AddParameter(command, "@name", account.Name);
|
||||
AddParameter(command, "@normal_name", account.NormalName);
|
||||
AddParameter(command, "@hash", account.Hash);
|
||||
AddParameter(command, "@mail", account.Mail);
|
||||
AddParameter(command, "@mail_verified", account.MailVerified);
|
||||
AddParameter(command, "@mail_verified_at", account.MailVerifiedAt);
|
||||
AddParameter(command, "@mail_token", account.MailToken);
|
||||
AddParameter(command, "@password_token", account.PasswordToken);
|
||||
AddParameterEnumInt32(command, "@state", account.State);
|
||||
AddParameter(command, "@last_login", account.LastLogin);
|
||||
AddParameter(command, "@created", account.Created);
|
||||
AddParameter(command, "@id", account.Id);
|
||||
});
|
||||
return rowsAffected > NoRowsAffected;
|
||||
}
|
||||
|
||||
public bool DeleteAccount(int accountId)
|
||||
{
|
||||
int rowsAffected = ExecuteNonQuery(SqlDeleteAccount,
|
||||
command => { AddParameter(command, "@id", accountId); });
|
||||
return rowsAffected > NoRowsAffected;
|
||||
}
|
||||
|
||||
private Account ReadAccount(DbDataReader reader)
|
||||
{
|
||||
Account account = new Account();
|
||||
account.Id = GetInt32(reader, "id");
|
||||
account.Name = GetString(reader, "name");
|
||||
account.NormalName = GetString(reader, "normal_name");
|
||||
account.Hash = GetString(reader, "hash");
|
||||
account.Mail = GetString(reader, "mail");
|
||||
account.MailVerified = GetBoolean(reader, "mail_verified");
|
||||
account.MailVerifiedAt = GetDateTimeNullable(reader, "mail_verified_at");
|
||||
account.MailToken = GetStringNullable(reader, "mail_token");
|
||||
account.PasswordToken = GetStringNullable(reader, "password_token");
|
||||
account.State = (AccountStateType) GetInt32(reader, "state");
|
||||
account.LastLogin = GetDateTimeNullable(reader, "last_login");
|
||||
account.Created = GetDateTime(reader, "created");
|
||||
return account;
|
||||
}
|
||||
}
|
||||
}
|
55
Mhf.Server/Database/Sql/MhfMariaDb.cs
Normal file
55
Mhf.Server/Database/Sql/MhfMariaDb.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using MySql.Data.MySqlClient;
|
||||
using Mhf.Server.Database.Sql.Core;
|
||||
|
||||
namespace Mhf.Server.Database.Sql
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite Mhf database.
|
||||
/// </summary>
|
||||
public class MhfMariaDb : MhfSqlDb<MySqlConnection, MySqlCommand>, IDatabase
|
||||
{
|
||||
public const string MemoryDatabasePath = ":memory:";
|
||||
|
||||
private const string SelectAutoIncrement = "SELECT last_insert_rowid()";
|
||||
|
||||
|
||||
private string _connectionString;
|
||||
|
||||
public bool CreateDatabase()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public MhfMariaDb(string host, short port, string user, string password, string database)
|
||||
{
|
||||
_connectionString = $"host={host};port={port};user id={user};password={password};database={database};";
|
||||
}
|
||||
|
||||
protected override MySqlConnection Connection()
|
||||
{
|
||||
MySqlConnection connection = new MySqlConnection(_connectionString);
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
|
||||
protected override MySqlCommand Command(string query, MySqlConnection connection)
|
||||
{
|
||||
MySqlCommand command = connection.CreateCommand();
|
||||
command.CommandText = query;
|
||||
return command;
|
||||
}
|
||||
|
||||
protected override long AutoIncrement(MySqlConnection connection, MySqlCommand command)
|
||||
{
|
||||
return command.LastInsertedId;
|
||||
}
|
||||
|
||||
public override int Upsert(string table, string[] columns, object[] values, string whereColumn,
|
||||
object whereValue,
|
||||
out long autoIncrement)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
96
Mhf.Server/Database/Sql/MhfSqLiteDb.cs
Normal file
96
Mhf.Server/Database/Sql/MhfSqLiteDb.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using Mhf.Server.Database.Sql.Core;
|
||||
|
||||
namespace Mhf.Server.Database.Sql
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite Mhf database.
|
||||
/// </summary>
|
||||
public class MhfSqLiteDb : MhfSqlDb<SQLiteConnection, SQLiteCommand>, IDatabase
|
||||
{
|
||||
public const string MemoryDatabasePath = ":memory:";
|
||||
public const int Version = 1;
|
||||
|
||||
private const string SelectAutoIncrement = "SELECT last_insert_rowid()";
|
||||
|
||||
private readonly string _databasePath;
|
||||
private string _connectionString;
|
||||
|
||||
public MhfSqLiteDb(string databasePath)
|
||||
{
|
||||
_databasePath = databasePath;
|
||||
Logger.Info($"Database Path: {_databasePath}");
|
||||
}
|
||||
|
||||
public bool CreateDatabase()
|
||||
{
|
||||
if (_databasePath != MemoryDatabasePath && !File.Exists(_databasePath))
|
||||
{
|
||||
FileStream fs = File.Create(_databasePath);
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
Logger.Info($"Created new v{Version} database");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string BuildConnectionString(string source)
|
||||
{
|
||||
SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
|
||||
builder.DataSource = source;
|
||||
builder.Version = 3;
|
||||
builder.ForeignKeys = true;
|
||||
// Set ADO.NET conformance flag https://system.data.sqlite.org/index.html/info/e36e05e299
|
||||
builder.Flags = builder.Flags & SQLiteConnectionFlags.StrictConformance;
|
||||
|
||||
string connectionString = builder.ToString();
|
||||
Logger.Info($"Connection String: {connectionString}");
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
protected override SQLiteConnection Connection()
|
||||
{
|
||||
if (_connectionString == null)
|
||||
{
|
||||
_connectionString = BuildConnectionString(_databasePath);
|
||||
}
|
||||
|
||||
SQLiteConnection connection = new SQLiteConnection(_connectionString);
|
||||
return connection.OpenAndReturn();
|
||||
}
|
||||
|
||||
protected override SQLiteCommand Command(string query, SQLiteConnection connection)
|
||||
{
|
||||
return new SQLiteCommand(query, connection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thread Safe on Connection basis.
|
||||
/// http://www.sqlite.org/c3ref/last_insert_rowid.html
|
||||
/// </summary>
|
||||
protected override long AutoIncrement(SQLiteConnection connection, SQLiteCommand command)
|
||||
{
|
||||
return connection.LastInsertRowId;
|
||||
// long autoIncrement = NoAutoIncrement;
|
||||
// ExecuteReader(SelectAutoIncrement, reader =>
|
||||
// {
|
||||
// if (reader.Read())
|
||||
// {
|
||||
// autoIncrement = reader.GetInt32(0);
|
||||
// }
|
||||
// });
|
||||
// return autoIncrement;
|
||||
}
|
||||
|
||||
public override int Upsert(string table, string[] columns, object[] values, string whereColumn,
|
||||
object whereValue,
|
||||
out long autoIncrement)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
87
Mhf.Server/Database/Sql/ScriptRunner.cs
Normal file
87
Mhf.Server/Database/Sql/ScriptRunner.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Arrowgene.Services.Logging;
|
||||
|
||||
namespace Mhf.Server.Database.Sql
|
||||
{
|
||||
public class ScriptRunner
|
||||
{
|
||||
private const string DEFAULT_DELIMITER = ";";
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private IDatabase _database;
|
||||
private string delimiter = DEFAULT_DELIMITER;
|
||||
private bool fullLineDelimiter = false;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public ScriptRunner(IDatabase database)
|
||||
{
|
||||
_database = database;
|
||||
_logger = LogProvider.Logger(this);
|
||||
}
|
||||
|
||||
public void Run(string path)
|
||||
{
|
||||
int index = 0;
|
||||
try
|
||||
{
|
||||
string[] file = File.ReadAllLines(path);
|
||||
StringBuilder command = null;
|
||||
for (; index < file.Length; index++)
|
||||
{
|
||||
string line = file[index];
|
||||
if (command == null)
|
||||
{
|
||||
command = new StringBuilder();
|
||||
}
|
||||
|
||||
string trimmedLine = line.Trim();
|
||||
|
||||
if (trimmedLine.Length < 1)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else if (trimmedLine.StartsWith("//") || trimmedLine.StartsWith("--"))
|
||||
{
|
||||
// Print comment
|
||||
}
|
||||
else if (!fullLineDelimiter && trimmedLine.EndsWith(delimiter)
|
||||
|| fullLineDelimiter && trimmedLine == delimiter)
|
||||
{
|
||||
command.Append(
|
||||
line.Substring(0, line.LastIndexOf(delimiter, StringComparison.InvariantCulture)));
|
||||
command.Append(" ");
|
||||
_database.Execute(command.ToString());
|
||||
command = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
command.Append(line);
|
||||
command.Append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (command != null)
|
||||
{
|
||||
string cmd = command.ToString();
|
||||
if (string.IsNullOrWhiteSpace(cmd))
|
||||
{
|
||||
//do nothing;
|
||||
}
|
||||
else
|
||||
{
|
||||
_database.Execute(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error($"Sql error at Line: {index}");
|
||||
_logger.Exception(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
286
Mhf.Server/Database/Sql/SqlDb.cs
Normal file
286
Mhf.Server/Database/Sql/SqlDb.cs
Normal file
@ -0,0 +1,286 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace Mhf.Server.Database.Sql
|
||||
{
|
||||
/// <summary>
|
||||
/// Operations for SQL type databases.
|
||||
/// </summary>
|
||||
public abstract class SqlDb<TCon, TCom>
|
||||
where TCon : DbConnection
|
||||
where TCom : DbCommand
|
||||
{
|
||||
public const int NoRowsAffected = 0;
|
||||
public const long NoAutoIncrement = 0;
|
||||
|
||||
public SqlDb()
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract TCon Connection();
|
||||
protected abstract TCom Command(string query, TCon connection);
|
||||
protected abstract long AutoIncrement(TCon connection, TCom command);
|
||||
|
||||
public abstract int Upsert(string table, string[] columns, object[] values, string whereColumn,
|
||||
object whereValue, out long autoIncrement);
|
||||
|
||||
public int ExecuteNonQuery(string query, Action<TCom> nonQueryAction)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (TCon connection = Connection())
|
||||
{
|
||||
using (TCom command = Command(query, connection))
|
||||
{
|
||||
nonQueryAction(command);
|
||||
int rowsAffected = command.ExecuteNonQuery();
|
||||
return rowsAffected;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception(ex);
|
||||
return NoRowsAffected;
|
||||
}
|
||||
}
|
||||
|
||||
public int ExecuteNonQuery(string query, Action<TCom> nonQueryAction, out long autoIncrement)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (TCon connection = Connection())
|
||||
{
|
||||
using (TCom command = Command(query, connection))
|
||||
{
|
||||
nonQueryAction(command);
|
||||
int rowsAffected = command.ExecuteNonQuery();
|
||||
autoIncrement = AutoIncrement(connection, command);
|
||||
return rowsAffected;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception(ex);
|
||||
autoIncrement = NoAutoIncrement;
|
||||
return NoRowsAffected;
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteReader(string query, Action<TCom> nonQueryAction, Action<DbDataReader> readAction)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (TCon connection = Connection())
|
||||
{
|
||||
using (TCom command = Command(query, connection))
|
||||
{
|
||||
nonQueryAction(command);
|
||||
using (DbDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
readAction(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteReader(string query, Action<DbDataReader> readAction)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (TCon connection = Connection())
|
||||
{
|
||||
using (TCom command = Command(query, connection))
|
||||
{
|
||||
using (DbDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
readAction(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(string query)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (TCon connection = Connection())
|
||||
{
|
||||
using (TCom command = Command(query, connection))
|
||||
{
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public string ServerVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (TCon connection = Connection())
|
||||
{
|
||||
return connection.ServerVersion;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception(ex);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Exception(Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
||||
protected DbParameter Parameter(TCom command, string name, object value, DbType type)
|
||||
{
|
||||
DbParameter parameter = command.CreateParameter();
|
||||
parameter.ParameterName = name;
|
||||
parameter.Value = value;
|
||||
parameter.DbType = type;
|
||||
return parameter;
|
||||
}
|
||||
|
||||
protected DbParameter Parameter(TCom command, string name, string value)
|
||||
{
|
||||
return Parameter(command, name, value, DbType.String);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, object value, DbType type)
|
||||
{
|
||||
DbParameter parameter = Parameter(command, name, value, type);
|
||||
command.Parameters.Add(parameter);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, string value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.String);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, Int32 value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.Int32);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, float value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.Double);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, byte value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.Byte);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, UInt32 value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.UInt32);
|
||||
}
|
||||
|
||||
protected void AddParameterEnumInt32<T>(TCom command, string name, T value) where T : Enum
|
||||
{
|
||||
AddParameter(command, name, (Int32) (object) value, DbType.Int32);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, DateTime? value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.DateTime);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, DateTime value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.DateTime);
|
||||
}
|
||||
|
||||
protected void AddParameter(TCom command, string name, bool value)
|
||||
{
|
||||
AddParameter(command, name, value, DbType.Boolean);
|
||||
}
|
||||
|
||||
protected DateTime? GetDateTimeNullable(DbDataReader reader, int ordinal)
|
||||
{
|
||||
if (reader.IsDBNull(ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return reader.GetDateTime(ordinal);
|
||||
}
|
||||
|
||||
protected string GetStringNullable(DbDataReader reader, int ordinal)
|
||||
{
|
||||
if (reader.IsDBNull(ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return reader.GetString(ordinal);
|
||||
}
|
||||
|
||||
protected int GetInt32(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetInt32(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected byte GetByte(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetByte(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected short GetInt16(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetInt16(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected float GetFloat(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetFloat(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected string GetString(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetString(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected bool GetBoolean(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetBoolean(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected DateTime GetDateTime(DbDataReader reader, string column)
|
||||
{
|
||||
return reader.GetDateTime(reader.GetOrdinal(column));
|
||||
}
|
||||
|
||||
protected DateTime? GetDateTimeNullable(DbDataReader reader, string column)
|
||||
{
|
||||
int ordinal = reader.GetOrdinal(column);
|
||||
return GetDateTimeNullable(reader, ordinal);
|
||||
}
|
||||
|
||||
protected string GetStringNullable(DbDataReader reader, string column)
|
||||
{
|
||||
int ordinal = reader.GetOrdinal(column);
|
||||
return GetStringNullable(reader, ordinal);
|
||||
}
|
||||
}
|
||||
}
|
2
Mhf.Server/Files/Database/Script/data_account.sql
Normal file
2
Mhf.Server/Files/Database/Script/data_account.sql
Normal file
@ -0,0 +1,2 @@
|
||||
INSERT INTO `account` (id,name,normal_name,hash,mail,mail_verified,mail_verified_at,mail_token,password_token,state,last_login,created) VALUES
|
||||
(1,'admin','admin','$2a$10$q38QrceMiKLxI3rj6zGpHOQTqG61pEbu5wExo6bhV3s5srxbg11Oi','admin',0,NULL,NULL,NULL,1,NULL,'2019-11-09 20:23:33.2347542');
|
28
Mhf.Server/Files/Database/Script/schema_mariadb.sql
Normal file
28
Mhf.Server/Files/Database/Script/schema_mariadb.sql
Normal file
@ -0,0 +1,28 @@
|
||||
CREATE TABLE IF NOT EXISTS `setting` (
|
||||
`key` VARCHAR(255) NOT NULL,
|
||||
`value` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `account` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(17) NOT NULL,
|
||||
`normal_name` VARCHAR(17) NOT NULL,
|
||||
`hash` VARCHAR(255) NOT NULL,
|
||||
`mail` VARCHAR(255) NOT NULL,
|
||||
`mail_verified` TINYINT(1) NOT NULL,
|
||||
`mail_verified_at` DATETIME DEFAULT NULL,
|
||||
`mail_token` VARCHAR(255) DEFAULT NULL,
|
||||
`password_token` VARCHAR(255) DEFAULT NULL,
|
||||
`state` INT(11) NOT NULL,
|
||||
`last_login` DATETIME DEFAULT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_account_name` (`name`),
|
||||
UNIQUE KEY `uq_account_normal_name` (`normal_name`),
|
||||
UNIQUE KEY `uq_account_mail` (`mail`)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8;
|
23
Mhf.Server/Files/Database/Script/schema_sqlite.sql
Normal file
23
Mhf.Server/Files/Database/Script/schema_sqlite.sql
Normal file
@ -0,0 +1,23 @@
|
||||
CREATE TABLE IF NOT EXISTS `setting` (
|
||||
`key` TEXT NOT NULL,
|
||||
`value` TEXT NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `account` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`normal_name` TEXT NOT NULL,
|
||||
`hash` TEXT NOT NULL,
|
||||
`mail` TEXT NOT NULL,
|
||||
`mail_verified` INTEGER NOT NULL,
|
||||
`mail_verified_at` DATETIME DEFAULT NULL,
|
||||
`mail_token` TEXT DEFAULT NULL,
|
||||
`password_token` TEXT DEFAULT NULL,
|
||||
`state` INTEGER NOT NULL,
|
||||
`last_login` DATETIME DEFAULT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
CONSTRAINT `uq_account_name` UNIQUE (`name`),
|
||||
CONSTRAINT `uq_account_normal_name` UNIQUE (`normal_name`),
|
||||
CONSTRAINT `uq_account_mail` UNIQUE (`mail`)
|
||||
);
|
BIN
Mhf.Server/Files/mhf.pfx
Normal file
BIN
Mhf.Server/Files/mhf.pfx
Normal file
Binary file not shown.
46
Mhf.Server/Logging/MhfLogPacket.cs
Normal file
46
Mhf.Server/Logging/MhfLogPacket.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Mhf.Server.Packet;
|
||||
|
||||
namespace Mhf.Server.Logging
|
||||
{
|
||||
public class MhfLogPacket : MhfPacket
|
||||
{
|
||||
public MhfLogPacket(string clientIdentity, MhfPacket packet, MhfLogType logType)
|
||||
: base(packet.Header, packet.Data.Clone())
|
||||
{
|
||||
LogType = logType;
|
||||
TimeStamp = DateTime.Now;
|
||||
ClientIdentity = clientIdentity;
|
||||
}
|
||||
|
||||
public string ClientIdentity { get; }
|
||||
public MhfLogType LogType { get; }
|
||||
public DateTime TimeStamp { get; }
|
||||
public string Hex => Data.ToHexString(' ');
|
||||
public string Ascii => Data.ToAsciiString(true);
|
||||
|
||||
public string ToLogText()
|
||||
{
|
||||
String log = $"{ClientIdentity} Packet Log";
|
||||
log += Environment.NewLine;
|
||||
log += "----------";
|
||||
log += Environment.NewLine;
|
||||
log += $"[{TimeStamp:HH:mm:ss}][Typ:{LogType}]";
|
||||
log += Environment.NewLine;
|
||||
log += $"[Id:0x{Id:X2}|{Id}][BodyLen:{Data.Size}][{PacketIdName}]";
|
||||
log += Environment.NewLine;
|
||||
log += Header.ToLogText();
|
||||
log += Environment.NewLine;
|
||||
log += "ASCII:";
|
||||
log += Environment.NewLine;
|
||||
log += Ascii;
|
||||
log += Environment.NewLine;
|
||||
log += "HEX:";
|
||||
log += Environment.NewLine;
|
||||
log += Hex;
|
||||
log += Environment.NewLine;
|
||||
log += "----------";
|
||||
return log;
|
||||
}
|
||||
}
|
||||
}
|
10
Mhf.Server/Logging/MhfLogType.cs
Normal file
10
Mhf.Server/Logging/MhfLogType.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Mhf.Server.Logging
|
||||
{
|
||||
public enum MhfLogType
|
||||
{
|
||||
PacketIn = 1,
|
||||
PacketOut = 2,
|
||||
PacketUnhandled = 3,
|
||||
PacketError = 4
|
||||
}
|
||||
}
|
195
Mhf.Server/Logging/MhfLogger.cs
Normal file
195
Mhf.Server/Logging/MhfLogger.cs
Normal file
@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Arrowgene.Services.Networking.Tcp;
|
||||
using Mhf.Server.Model;
|
||||
using Mhf.Server.Packet;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Server.Logging
|
||||
{
|
||||
public class MhfLogger : Logger
|
||||
{
|
||||
private MhfSetting _setting;
|
||||
|
||||
public MhfLogger() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public MhfLogger(string identity, string zone = null) : base(identity, zone)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Initialize(string identity, string zone, object configuration)
|
||||
{
|
||||
base.Initialize(identity, zone, configuration);
|
||||
_setting = configuration as MhfSetting;
|
||||
if (_setting == null)
|
||||
{
|
||||
Error("Couldn't apply MhfLogger configuration");
|
||||
}
|
||||
}
|
||||
|
||||
public void Info(MhfClient client, string message, params object[] args)
|
||||
{
|
||||
Write(LogLevel.Info, null, $"{client.Identity} {message}", args);
|
||||
}
|
||||
|
||||
public void Info(MhfConnection connection, string message, params object[] args)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
Info(client, message, args);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(LogLevel.Info, null, $"{connection.Identity} {message}", args);
|
||||
}
|
||||
|
||||
public void Debug(MhfClient client, string message, params object[] args)
|
||||
{
|
||||
Write(LogLevel.Debug, null, $"{client.Identity} {message}", args);
|
||||
}
|
||||
|
||||
public void Error(MhfClient client, string message, params object[] args)
|
||||
{
|
||||
Write(LogLevel.Error, null, $"{client.Identity} {message}", args);
|
||||
}
|
||||
|
||||
public void Error(MhfConnection connection, string message, params object[] args)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
Error(client, message, args);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(LogLevel.Error, null, $"{connection.Identity} {message}", args);
|
||||
}
|
||||
|
||||
public void Exception(MhfClient client, Exception exception)
|
||||
{
|
||||
Write(LogLevel.Error, null, $"{client.Identity} {exception}");
|
||||
}
|
||||
|
||||
public void Exception(MhfConnection connection, Exception exception)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
Exception(client, exception);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(LogLevel.Error, null, $"{connection.Identity} {exception}");
|
||||
}
|
||||
|
||||
public void Info(ITcpSocket socket, string message, params object[] args)
|
||||
{
|
||||
Write(LogLevel.Info, null, $"[{socket.Identity}] {message}", args);
|
||||
}
|
||||
|
||||
public void Debug(ITcpSocket socket, string message, params object[] args)
|
||||
{
|
||||
Write(LogLevel.Debug, null, $"[{socket.Identity}] {message}", args);
|
||||
}
|
||||
|
||||
public void Error(ITcpSocket socket, string message, params object[] args)
|
||||
{
|
||||
Write(LogLevel.Error, null, $"[{socket.Identity}] {message}", args);
|
||||
}
|
||||
|
||||
public void Exception(ITcpSocket socket, Exception exception)
|
||||
{
|
||||
Write(LogLevel.Error, null, $"[{socket.Identity}] {exception}");
|
||||
}
|
||||
|
||||
public void LogIncomingPacket(MhfClient client, MhfPacket packet)
|
||||
{
|
||||
if (_setting.LogIncomingPackets)
|
||||
{
|
||||
MhfLogPacket logPacket = new MhfLogPacket(client.Identity, packet, MhfLogType.PacketIn);
|
||||
WritePacket(logPacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void LogIncomingPacket(MhfConnection connection, MhfPacket packet)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
LogIncomingPacket(client, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_setting.LogIncomingPackets)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MhfLogPacket logPacket = new MhfLogPacket(connection.Identity, packet, MhfLogType.PacketIn);
|
||||
WritePacket(logPacket);
|
||||
}
|
||||
|
||||
public void LogUnknownIncomingPacket(MhfClient client, MhfPacket packet)
|
||||
{
|
||||
if (_setting.LogUnknownIncomingPackets)
|
||||
{
|
||||
MhfLogPacket logPacket = new MhfLogPacket(client.Identity, packet, MhfLogType.PacketUnhandled);
|
||||
WritePacket(logPacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void LogUnknownIncomingPacket(MhfConnection connection, MhfPacket packet)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
LogUnknownIncomingPacket(client, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_setting.LogIncomingPackets)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MhfLogPacket logPacket =
|
||||
new MhfLogPacket(connection.Identity, packet, MhfLogType.PacketUnhandled);
|
||||
WritePacket(logPacket);
|
||||
}
|
||||
|
||||
public void LogOutgoingPacket(MhfClient client, MhfPacket packet)
|
||||
{
|
||||
if (_setting.LogOutgoingPackets)
|
||||
{
|
||||
MhfLogPacket logPacket = new MhfLogPacket(client.Identity, packet, MhfLogType.PacketOut);
|
||||
WritePacket(logPacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void LogOutgoingPacket(MhfConnection connection, MhfPacket packet)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
LogOutgoingPacket(client, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_setting.LogIncomingPackets)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MhfLogPacket logPacket = new MhfLogPacket(connection.Identity, packet, MhfLogType.PacketOut);
|
||||
WritePacket(logPacket);
|
||||
}
|
||||
|
||||
private void WritePacket(MhfLogPacket packet)
|
||||
{
|
||||
Write(LogLevel.Info, packet, packet.ToLogText());
|
||||
}
|
||||
}
|
||||
}
|
25
Mhf.Server/Mhf.Server.csproj
Normal file
25
Mhf.Server/Mhf.Server.csproj
Normal file
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<RootNamespace>Mhf.Server</RootNamespace>
|
||||
<Title>Monster Hunter Frontier Z Server</Title>
|
||||
<Company>Mhf Team</Company>
|
||||
<Product>Mhf.Server</Product>
|
||||
<Version>$(Version)</Version>
|
||||
<Copyright>Copyright © 2019 Mhf Team</Copyright>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Arrowgene.Services" Version="1.12.1" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.112" />
|
||||
<PackageReference Include="MySqlConnector" Version="0.61.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next.StrongName" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="Files\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>Files\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
</Project>
|
132
Mhf.Server/MhfServer.cs
Normal file
132
Mhf.Server/MhfServer.cs
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* This file is part of Mhf.Server
|
||||
*
|
||||
* Mhf.Server is a server implementation for the game "Monster Hunter Frontier Z".
|
||||
* Copyright (C) 2019-2020 Mhf Team
|
||||
*
|
||||
* Github: https://github.com/sebastian-heinz/mhf-server
|
||||
*
|
||||
* Mhf.Server is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Mhf.Server 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Mhf.Server. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Arrowgene.Services.Logging;
|
||||
using Arrowgene.Services.Networking.Tcp.Server.AsyncEvent;
|
||||
using Mhf.Server.Common.Instance;
|
||||
using Mhf.Server.Database;
|
||||
using Mhf.Server.Logging;
|
||||
using Mhf.Server.Model;
|
||||
using Mhf.Server.Packet;
|
||||
using Mhf.Server.PacketHandler;
|
||||
using Mhf.Server.Setting;
|
||||
using Mhf.Server.Web;
|
||||
using Mhf.Server.Web.Server.Kestrel;
|
||||
using Mhf.Server.WebMiddlewares;
|
||||
using Mhf.Server.WebRoutes;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace Mhf.Server
|
||||
{
|
||||
public class MhfServer
|
||||
{
|
||||
public MhfSetting Setting { get; }
|
||||
public PacketRouter Router { get; }
|
||||
public ClientLookup Clients { get; }
|
||||
public IDatabase Database { get; }
|
||||
public InstanceGenerator Instances { get; }
|
||||
|
||||
public IFileProvider WebFileProvider { get; }
|
||||
|
||||
private readonly QueueConsumer _authConsumer;
|
||||
private readonly QueueConsumer _lobbyConsumer;
|
||||
private readonly AsyncEventServer _authServer;
|
||||
private readonly AsyncEventServer _lobbyServer;
|
||||
private readonly WebServer _webServer;
|
||||
private readonly MhfLogger _logger;
|
||||
|
||||
public MhfServer(MhfSetting setting)
|
||||
{
|
||||
Setting = new MhfSetting(setting);
|
||||
|
||||
LogProvider.Configure<MhfLogger>(Setting);
|
||||
_logger = LogProvider.Logger<MhfLogger>(this);
|
||||
|
||||
Instances = new InstanceGenerator();
|
||||
Clients = new ClientLookup();
|
||||
Router = new PacketRouter();
|
||||
Database = new MhfDatabaseBuilder().Build(Setting.DatabaseSetting);
|
||||
|
||||
_authConsumer = new QueueConsumer(Setting, Setting.ServerSocketSettings);
|
||||
_authConsumer.ClientDisconnected += AuthClientDisconnected;
|
||||
_authServer = new AsyncEventServer(
|
||||
Setting.ListenIpAddress,
|
||||
Setting.AuthServerPort,
|
||||
_authConsumer,
|
||||
Setting.ServerSocketSettings
|
||||
);
|
||||
|
||||
_lobbyConsumer= new QueueConsumer(Setting, Setting.ServerSocketSettings);
|
||||
_lobbyConsumer.ClientDisconnected += LobbyClientDisconnected;
|
||||
_lobbyServer = new AsyncEventServer(
|
||||
Setting.ListenIpAddress,
|
||||
Setting.LobbyServerPort,
|
||||
_lobbyConsumer,
|
||||
Setting.ServerSocketSettings
|
||||
);
|
||||
|
||||
_webServer = new WebServer(Setting, new KestrelWebServer(Setting));
|
||||
|
||||
WebFileProvider = new PhysicalFileProvider(Setting.WebSetting.WebFolder);
|
||||
|
||||
LoadPacketHandler();
|
||||
LoadWebRoutes();
|
||||
}
|
||||
|
||||
private void AuthClientDisconnected(MhfConnection client)
|
||||
{
|
||||
}
|
||||
|
||||
private void LobbyClientDisconnected(MhfConnection client)
|
||||
{
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_authServer.Start();
|
||||
_webServer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_authServer.Stop();
|
||||
_webServer.Stop();
|
||||
}
|
||||
|
||||
private void LoadPacketHandler()
|
||||
{
|
||||
_authConsumer.AddHandler(new MsgHeadHandler(this));
|
||||
}
|
||||
|
||||
private void LoadWebRoutes()
|
||||
{
|
||||
_webServer.AddRoute(new IndexRoute());
|
||||
_webServer.AddRoute(new AuthLauncherLoginRoute(WebFileProvider));
|
||||
_webServer.AddRoute(new AuthLauncherStartRoute(WebFileProvider));
|
||||
_webServer.AddRoute(new LauncherIndexRoute(WebFileProvider));
|
||||
_webServer.AddRoute(new MhfFileRoute(WebFileProvider));
|
||||
|
||||
// Middleware - Order Matters
|
||||
_webServer.AddMiddleware(new StaticFileMiddleware("", WebFileProvider));
|
||||
}
|
||||
}
|
||||
}
|
25
Mhf.Server/Model/Account.cs
Normal file
25
Mhf.Server/Model/Account.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
public class Account
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string NormalName { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Mail { get; set; }
|
||||
public string MailToken { get; set; }
|
||||
public string PasswordToken { get; set; }
|
||||
public bool MailVerified { get; set; }
|
||||
public DateTime? MailVerifiedAt { get; set; }
|
||||
public AccountStateType State { get; set; }
|
||||
public DateTime? LastLogin { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
public Account()
|
||||
{
|
||||
Id = -1;
|
||||
}
|
||||
}
|
||||
}
|
11
Mhf.Server/Model/AccountStateType.cs
Normal file
11
Mhf.Server/Model/AccountStateType.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
public enum AccountStateType
|
||||
{
|
||||
Banned = 0,
|
||||
None = 0,
|
||||
User = 1,
|
||||
GameMaster = 50,
|
||||
Admin = 100
|
||||
}
|
||||
}
|
71
Mhf.Server/Model/ClientLookup.cs
Normal file
71
Mhf.Server/Model/ClientLookup.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
public class ClientLookup
|
||||
{
|
||||
private readonly List<MhfClient> _clients;
|
||||
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public ClientLookup()
|
||||
{
|
||||
_clients = new List<MhfClient>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Clients.
|
||||
/// </summary>
|
||||
public List<MhfClient> GetAll()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return new List<MhfClient>(_clients);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Client.
|
||||
/// </summary>
|
||||
public void Add(MhfClient client)
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_clients.Add(client);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Client from all lists and lookup tables.
|
||||
/// </summary>
|
||||
public void Remove(MhfClient client)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_clients.Remove(client);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Client by AccountId if it exists.
|
||||
/// </summary>
|
||||
public MhfClient GetByAccountId(int accountId)
|
||||
{
|
||||
List<MhfClient> clients = GetAll();
|
||||
foreach (MhfClient client in clients)
|
||||
{
|
||||
if (client.Account.Id == accountId)
|
||||
{
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
7
Mhf.Server/Model/DatabaseType.cs
Normal file
7
Mhf.Server/Model/DatabaseType.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
public enum DatabaseType
|
||||
{
|
||||
SQLite,
|
||||
}
|
||||
}
|
9
Mhf.Server/Model/ISender.cs
Normal file
9
Mhf.Server/Model/ISender.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Mhf.Server.Packet;
|
||||
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
public interface ISender
|
||||
{
|
||||
void Send(MhfPacket packet);
|
||||
}
|
||||
}
|
52
Mhf.Server/Model/MhfClient.cs
Normal file
52
Mhf.Server/Model/MhfClient.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Logging;
|
||||
using Mhf.Server.Packet;
|
||||
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
[DebuggerDisplay("{Identity,nq}")]
|
||||
public class MhfClient : ISender
|
||||
{
|
||||
private readonly MhfLogger _logger;
|
||||
|
||||
public MhfClient()
|
||||
{
|
||||
_logger = LogProvider.Logger<MhfLogger>(this);
|
||||
Creation = DateTime.Now;
|
||||
Identity = "";
|
||||
}
|
||||
|
||||
public DateTime Creation { get; }
|
||||
public string Identity { get; private set; }
|
||||
public Account Account { get; set; }
|
||||
public MhfConnection Connection { get; set; }
|
||||
|
||||
|
||||
public void Send(MhfPacket packet)
|
||||
{
|
||||
Connection.Send(packet);
|
||||
}
|
||||
|
||||
public void UpdateIdentity()
|
||||
{
|
||||
Identity = "";
|
||||
if (Account != null)
|
||||
{
|
||||
Identity += $"[Acc:{Account.Id}:{Account.Name}]";
|
||||
return;
|
||||
}
|
||||
|
||||
if (Connection != null)
|
||||
{
|
||||
Identity += $"[Con:{Connection.Identity}]";
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Connection?.Socket.Close();
|
||||
}
|
||||
}
|
||||
}
|
60
Mhf.Server/Model/MhfConnection.cs
Normal file
60
Mhf.Server/Model/MhfConnection.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Arrowgene.Services.Networking.Tcp;
|
||||
using Mhf.Server.Logging;
|
||||
using Mhf.Server.Packet;
|
||||
|
||||
namespace Mhf.Server.Model
|
||||
{
|
||||
public class MhfConnection : ISender
|
||||
{
|
||||
private readonly MhfLogger _logger;
|
||||
|
||||
public MhfConnection(ITcpSocket clientSocket, PacketFactory packetFactory)
|
||||
{
|
||||
_logger = LogProvider.Logger<MhfLogger>(this);
|
||||
Socket = clientSocket;
|
||||
PacketFactory = packetFactory;
|
||||
Client = null;
|
||||
}
|
||||
|
||||
public string Identity => Socket.Identity;
|
||||
public ITcpSocket Socket { get; }
|
||||
public PacketFactory PacketFactory { get; }
|
||||
public MhfClient Client { get; set; }
|
||||
|
||||
public List<MhfPacket> Receive(byte[] data)
|
||||
{
|
||||
List<MhfPacket> packets;
|
||||
try
|
||||
{
|
||||
packets = PacketFactory.Read(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(this, ex);
|
||||
packets = new List<MhfPacket>();
|
||||
}
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
public void Send(MhfPacket packet)
|
||||
{
|
||||
byte[] data;
|
||||
try
|
||||
{
|
||||
data = PacketFactory.Write(packet);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(this, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// _logger.LogOutgoingPacket(this, packet);
|
||||
Socket.Send(data);
|
||||
}
|
||||
}
|
||||
}
|
13
Mhf.Server/Packet/ClientHandler.cs
Normal file
13
Mhf.Server/Packet/ClientHandler.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public abstract class ClientHandler : Handler, IClientHandler
|
||||
{
|
||||
protected ClientHandler(MhfServer server) : base(server)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Handle(MhfClient client, MhfPacket packet);
|
||||
}
|
||||
}
|
22
Mhf.Server/Packet/ClientHandlerDeserializer.cs
Normal file
22
Mhf.Server/Packet/ClientHandlerDeserializer.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public abstract class ClientHandlerDeserializer<T> : ClientHandler
|
||||
{
|
||||
private readonly IPacketDeserializer<T> _deserializer;
|
||||
|
||||
protected ClientHandlerDeserializer(MhfServer server, IPacketDeserializer<T> deserializer) : base(server)
|
||||
{
|
||||
_deserializer = deserializer;
|
||||
}
|
||||
|
||||
public override void Handle(MhfClient client, MhfPacket requestPacket)
|
||||
{
|
||||
T request = _deserializer.Deserialize(requestPacket);
|
||||
HandleRequest(client, request);
|
||||
}
|
||||
|
||||
public abstract void HandleRequest(MhfClient client, T request);
|
||||
}
|
||||
}
|
13
Mhf.Server/Packet/ConnectionHandler.cs
Normal file
13
Mhf.Server/Packet/ConnectionHandler.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public abstract class ConnectionHandler : Handler, IConnectionHandler
|
||||
{
|
||||
protected ConnectionHandler(MhfServer server) : base(server)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Handle(MhfConnection client, MhfPacket packet);
|
||||
}
|
||||
}
|
30
Mhf.Server/Packet/Handler.cs
Normal file
30
Mhf.Server/Packet/Handler.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Database;
|
||||
using Mhf.Server.Logging;
|
||||
using Mhf.Server.Model;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public abstract class Handler : IHandler
|
||||
{
|
||||
protected Handler(MhfServer server)
|
||||
{
|
||||
Logger = LogProvider.Logger<MhfLogger>(this);
|
||||
Server = server;
|
||||
Router = server.Router;
|
||||
Database = server.Database;
|
||||
Settings = server.Setting;
|
||||
Clients = server.Clients;
|
||||
}
|
||||
|
||||
public abstract ushort Id { get; }
|
||||
public virtual int ExpectedSize => QueueConsumer.NoExpectedSize;
|
||||
protected MhfServer Server { get; }
|
||||
protected MhfSetting Settings { get; }
|
||||
protected MhfLogger Logger { get; }
|
||||
protected PacketRouter Router { get; }
|
||||
protected ClientLookup Clients { get; }
|
||||
protected IDatabase Database { get; }
|
||||
}
|
||||
}
|
9
Mhf.Server/Packet/IClientHandler.cs
Normal file
9
Mhf.Server/Packet/IClientHandler.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public interface IClientHandler : IHandler
|
||||
{
|
||||
void Handle(MhfClient client, MhfPacket packet);
|
||||
}
|
||||
}
|
9
Mhf.Server/Packet/IConnectionHandler.cs
Normal file
9
Mhf.Server/Packet/IConnectionHandler.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public interface IConnectionHandler : IHandler
|
||||
{
|
||||
void Handle(MhfConnection connection, MhfPacket packet);
|
||||
}
|
||||
}
|
8
Mhf.Server/Packet/IHandler.cs
Normal file
8
Mhf.Server/Packet/IHandler.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public interface IHandler
|
||||
{
|
||||
ushort Id { get; }
|
||||
int ExpectedSize { get; }
|
||||
}
|
||||
}
|
7
Mhf.Server/Packet/IPacketDeserializer.cs
Normal file
7
Mhf.Server/Packet/IPacketDeserializer.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public interface IPacketDeserializer<T>
|
||||
{
|
||||
T Deserialize(MhfPacket packet);
|
||||
}
|
||||
}
|
7
Mhf.Server/Packet/IPacketSerializer.cs
Normal file
7
Mhf.Server/Packet/IPacketSerializer.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public interface IPacketSerializer<T>
|
||||
{
|
||||
MhfPacket Serialize(T obj);
|
||||
}
|
||||
}
|
56
Mhf.Server/Packet/MhfPacket.cs
Normal file
56
Mhf.Server/Packet/MhfPacket.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Arrowgene.Services.Buffers;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public class MhfPacket
|
||||
{
|
||||
public static string GetPacketIdName(ushort id)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(PacketId), id))
|
||||
{
|
||||
PacketId authPacketId = (PacketId) id;
|
||||
return authPacketId.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string _packetIdName;
|
||||
|
||||
public MhfPacket(ushort id, IBuffer buffer)
|
||||
{
|
||||
Header = new PacketHeader(id);
|
||||
Data = buffer;
|
||||
}
|
||||
|
||||
public MhfPacket(PacketHeader header, IBuffer buffer)
|
||||
{
|
||||
Header = header;
|
||||
Data = buffer;
|
||||
}
|
||||
|
||||
public IBuffer Data { get; }
|
||||
public ushort Id => Header.Id;
|
||||
public PacketHeader Header { get; }
|
||||
|
||||
public string PacketIdName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_packetIdName != null)
|
||||
{
|
||||
return _packetIdName;
|
||||
}
|
||||
|
||||
_packetIdName = GetPacketIdName(Id);
|
||||
if (_packetIdName == null)
|
||||
{
|
||||
_packetIdName = $"ID_NOT_DEFINED_{Id}";
|
||||
}
|
||||
|
||||
return _packetIdName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
400
Mhf.Server/Packet/PacketFactory.cs
Normal file
400
Mhf.Server/Packet/PacketFactory.cs
Normal file
File diff suppressed because one or more lines are too long
39
Mhf.Server/Packet/PacketHeader.cs
Normal file
39
Mhf.Server/Packet/PacketHeader.cs
Normal file
@ -0,0 +1,39 @@
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public class PacketHeader
|
||||
{
|
||||
public PacketHeader(ushort id) : this(id, 0, 0, 0, 0, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
public PacketHeader(ushort id, byte pf0, byte keyRot, ushort dataSize, ushort combinedCheck, ushort check0,
|
||||
ushort check1,
|
||||
ushort check2)
|
||||
{
|
||||
Id = id;
|
||||
Pf0 = pf0;
|
||||
KeyRotDelta = keyRot;
|
||||
DataSize = dataSize;
|
||||
CombinedCheck = combinedCheck;
|
||||
Check0 = check0;
|
||||
Check1 = check1;
|
||||
Check2 = check2;
|
||||
}
|
||||
|
||||
public ushort Id { get; set; }
|
||||
public byte Pf0 { get; set; }
|
||||
public byte KeyRotDelta { get; set; }
|
||||
public ushort DataSize { get; set; }
|
||||
public ushort CombinedCheck { get; set; }
|
||||
public ushort Check0 { get; set; }
|
||||
public ushort Check1 { get; set; }
|
||||
public ushort Check2 { get; set; }
|
||||
|
||||
|
||||
public string ToLogText()
|
||||
{
|
||||
return
|
||||
$"[Pf0:0x{Pf0:X2}|KeyRotDelta:0x{KeyRotDelta:X2}|Id:0x{Id:X2}|DataSize:0x{DataSize:X2}|CombinedCheck:0x{CombinedCheck:X2}|Chk0:0x{Check0:X2}|Chk1:0x{Check1:X2}|Chk02:0x{Check2:X2}]";
|
||||
}
|
||||
}
|
||||
}
|
438
Mhf.Server/Packet/PacketId.cs
Normal file
438
Mhf.Server/Packet/PacketId.cs
Normal file
@ -0,0 +1,438 @@
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public enum PacketId : ushort
|
||||
{
|
||||
MSG_HEAD = 0,
|
||||
MSG_SYS_reserve01 = 1,
|
||||
MSG_SYS_reserve02 = 2,
|
||||
MSG_SYS_reserve03 = 3,
|
||||
MSG_SYS_reserve04 = 4,
|
||||
MSG_SYS_reserve05 = 5,
|
||||
MSG_SYS_reserve06 = 6,
|
||||
MSG_SYS_reserve07 = 7,
|
||||
MSG_SYS_ADD_OBJECT = 8,
|
||||
MSG_SYS_DEL_OBJECT = 9,
|
||||
MSG_SYS_DISP_OBJECT = 10,
|
||||
MSG_SYS_HIDE_OBJECT = 11,
|
||||
MSG_SYS_reserve0C = 12,
|
||||
MSG_SYS_reserve0D = 13,
|
||||
MSG_SYS_reserve0E = 14,
|
||||
MSG_SYS_EXTEND_THRESHOLD = 15,
|
||||
MSG_SYS_END = 16,
|
||||
MSG_SYS_NOP = 17,
|
||||
MSG_SYS_ACK = 18,
|
||||
MSG_SYS_TERMINAL_LOG = 19,
|
||||
MSG_SYS_LOGIN = 20,
|
||||
MSG_SYS_LOGOUT = 21,
|
||||
MSG_SYS_SET_STATUS = 22,
|
||||
MSG_SYS_PING = 23,
|
||||
MSG_SYS_CAST_BINARY = 24,
|
||||
MSG_SYS_HIDE_CLIENT = 25,
|
||||
MSG_SYS_TIME = 26,
|
||||
MSG_SYS_CASTED_BINARY = 27,
|
||||
MSG_SYS_GET_FILE = 28,
|
||||
MSG_SYS_ISSUE_LOGKEY = 29,
|
||||
MSG_SYS_RECORD_LOG = 30,
|
||||
MSG_SYS_ECHO = 31,
|
||||
MSG_SYS_CREATE_STAGE = 32,
|
||||
MSG_SYS_STAGE_DESTRUCT = 33,
|
||||
MSG_SYS_ENTER_STAGE = 34,
|
||||
MSG_SYS_BACK_STAGE = 35,
|
||||
MSG_SYS_MOVE_STAGE = 36,
|
||||
MSG_SYS_LEAVE_STAGE = 37,
|
||||
MSG_SYS_LOCK_STAGE = 38,
|
||||
MSG_SYS_UNLOCK_STAGE = 39,
|
||||
MSG_SYS_RESERVE_STAGE = 40,
|
||||
MSG_SYS_UNRESERVE_STAGE = 41,
|
||||
MSG_SYS_SET_STAGE_PASS = 42,
|
||||
MSG_SYS_WAIT_STAGE_BINARY = 43,
|
||||
MSG_SYS_SET_STAGE_BINARY = 44,
|
||||
MSG_SYS_GET_STAGE_BINARY = 45,
|
||||
MSG_SYS_ENUMERATE_CLIENT = 46,
|
||||
MSG_SYS_ENUMERATE_STAGE = 47,
|
||||
MSG_SYS_CREATE_MUTEX = 48,
|
||||
MSG_SYS_CREATE_OPEN_MUTEX = 49,
|
||||
MSG_SYS_DELETE_MUTEX = 50,
|
||||
MSG_SYS_OPEN_MUTEX = 51,
|
||||
MSG_SYS_CLOSE_MUTEX = 52,
|
||||
MSG_SYS_CREATE_SEMAPHORE = 53,
|
||||
MSG_SYS_CREATE_ACQUIRE_SEMAPHORE = 54,
|
||||
MSG_SYS_DELETE_SEMAPHORE = 55,
|
||||
MSG_SYS_ACQUIRE_SEMAPHORE = 56,
|
||||
MSG_SYS_RELEASE_SEMAPHORE = 57,
|
||||
MSG_SYS_LOCK_GLOBAL_SEMA = 58,
|
||||
MSG_SYS_UNLOCK_GLOBAL_SEMA = 59,
|
||||
MSG_SYS_CHECK_SEMAPHORE = 60,
|
||||
MSG_SYS_OPERATE_REGISTER = 61,
|
||||
MSG_SYS_LOAD_REGISTER = 62,
|
||||
MSG_SYS_NOTIFY_REGISTER = 63,
|
||||
MSG_SYS_CREATE_OBJECT = 64,
|
||||
MSG_SYS_DELETE_OBJECT = 65,
|
||||
MSG_SYS_POSITION_OBJECT = 66,
|
||||
MSG_SYS_ROTATE_OBJECT = 67,
|
||||
MSG_SYS_DUPLICATE_OBJECT = 68,
|
||||
MSG_SYS_SET_OBJECT_BINARY = 69,
|
||||
MSG_SYS_GET_OBJECT_BINARY = 70,
|
||||
MSG_SYS_GET_OBJECT_OWNER = 71,
|
||||
MSG_SYS_UPDATE_OBJECT_BINARY = 72,
|
||||
MSG_SYS_CLEANUP_OBJECT = 73,
|
||||
MSG_SYS_reserve4A = 74,
|
||||
MSG_SYS_reserve4B = 75,
|
||||
MSG_SYS_reserve4C = 76,
|
||||
MSG_SYS_reserve4D = 77,
|
||||
MSG_SYS_reserve4E = 78,
|
||||
MSG_SYS_reserve4F = 79,
|
||||
MSG_SYS_INSERT_USER = 80,
|
||||
MSG_SYS_DELETE_USER = 81,
|
||||
MSG_SYS_SET_USER_BINARY = 82,
|
||||
MSG_SYS_GET_USER_BINARY = 83,
|
||||
MSG_SYS_NOTIFY_USER_BINARY = 84,
|
||||
MSG_SYS_reserve55 = 85,
|
||||
MSG_SYS_reserve56 = 86,
|
||||
MSG_SYS_reserve57 = 87,
|
||||
MSG_SYS_UPDATE_RIGHT = 88,
|
||||
MSG_SYS_AUTH_QUERY = 89,
|
||||
MSG_SYS_AUTH_DATA = 90,
|
||||
MSG_SYS_AUTH_TERMINAL = 91,
|
||||
MSG_SYS_reserve5C = 92,
|
||||
MSG_SYS_RIGHTS_RELOAD = 93,
|
||||
MSG_SYS_reserve5E = 94,
|
||||
MSG_SYS_reserve5F = 95,
|
||||
MSG_MHF_SAVEDATA = 96,
|
||||
MSG_MHF_LOADDATA = 97,
|
||||
MSG_MHF_LIST_MEMBER = 98,
|
||||
MSG_MHF_OPR_MEMBER = 99,
|
||||
MSG_MHF_ENUMERATE_DIST_ITEM = 100,
|
||||
MSG_MHF_APPLY_DIST_ITEM = 101,
|
||||
MSG_MHF_ACQUIRE_DIST_ITEM = 102,
|
||||
MSG_MHF_GET_DIST_DESCRIPTION = 103,
|
||||
MSG_MHF_SEND_MAIL = 104,
|
||||
MSG_MHF_READ_MAIL = 105,
|
||||
MSG_MHF_LIST_MAIL = 106,
|
||||
MSG_MHF_OPRT_MAIL = 107,
|
||||
MSG_MHF_LOAD_FAVORITE_QUEST = 108,
|
||||
MSG_MHF_SAVE_FAVORITE_QUEST = 109,
|
||||
MSG_MHF_REGISTER_EVENT = 110,
|
||||
MSG_MHF_RELEASE_EVENT = 111,
|
||||
MSG_MHF_TRANSIT_MESSAGE = 112,
|
||||
MSG_SYS_reserve71 = 113,
|
||||
MSG_SYS_reserve72 = 114,
|
||||
MSG_SYS_reserve73 = 115,
|
||||
MSG_SYS_reserve74 = 116,
|
||||
MSG_SYS_reserve75 = 117,
|
||||
MSG_SYS_reserve76 = 118,
|
||||
MSG_SYS_reserve77 = 119,
|
||||
MSG_SYS_reserve78 = 120,
|
||||
MSG_SYS_reserve79 = 121,
|
||||
MSG_SYS_reserve7A = 122,
|
||||
MSG_SYS_reserve7B = 123,
|
||||
MSG_SYS_reserve7C = 124,
|
||||
MSG_CA_EXCHANGE_ITEM = 125,
|
||||
MSG_SYS_reserve7E = 126,
|
||||
MSG_MHF_PRESENT_BOX = 127,
|
||||
MSG_MHF_SERVER_COMMAND = 128,
|
||||
MSG_MHF_SHUT_CLIENT = 129,
|
||||
MSG_MHF_ANNOUNCE = 130,
|
||||
MSG_MHF_SET_LOGINWINDOW = 131,
|
||||
MSG_SYS_TRANS_BINARY = 132,
|
||||
MSG_SYS_COLLECT_BINARY = 133,
|
||||
MSG_SYS_GET_STATE = 134,
|
||||
MSG_SYS_SERIALIZE = 135,
|
||||
MSG_SYS_ENUMLOBBY = 136,
|
||||
MSG_SYS_ENUMUSER = 137,
|
||||
MSG_SYS_INFOKYSERVER = 138,
|
||||
MSG_MHF_GET_CA_UNIQUE_ID = 139,
|
||||
MSG_MHF_SET_CA_ACHIEVEMENT = 140,
|
||||
MSG_MHF_CARAVAN_MY_SCORE = 141,
|
||||
MSG_MHF_CARAVAN_RANKING = 142,
|
||||
MSG_MHF_CARAVAN_MY_RANK = 143,
|
||||
MSG_MHF_CREATE_GUILD = 144,
|
||||
MSG_MHF_OPERATE_GUILD = 145,
|
||||
MSG_MHF_OPERATE_GUILD_MEMBER = 146,
|
||||
MSG_MHF_INFO_GUILD = 147,
|
||||
MSG_MHF_ENUMERATE_GUILD = 148,
|
||||
MSG_MHF_UPDATE_GUILD = 149,
|
||||
MSG_MHF_ARRANGE_GUILD_MEMBER = 150,
|
||||
MSG_MHF_ENUMERATE_GUILD_MEMBER = 151,
|
||||
MSG_MHF_ENUMERATE_CAMPAIGN = 152,
|
||||
MSG_MHF_STATE_CAMPAIGN = 153,
|
||||
MSG_MHF_APPLY_CAMPAIGN = 154,
|
||||
MSG_MHF_ENUMERATE_ITEM = 155,
|
||||
MSG_MHF_ACQUIRE_ITEM = 156,
|
||||
MSG_MHF_TRANSFER_ITEM = 157,
|
||||
MSG_MHF_MERCENARY_HUNTDATA = 158,
|
||||
MSG_MHF_ENTRY_ROOKIE_GUILD = 159,
|
||||
MSG_MHF_ENUMERATE_QUEST = 160,
|
||||
MSG_MHF_ENUMERATE_EVENT = 161,
|
||||
MSG_MHF_ENUMERATE_PRICE = 162,
|
||||
MSG_MHF_ENUMERATE_RANKING = 163,
|
||||
MSG_MHF_ENUMERATE_ORDER = 164,
|
||||
MSG_MHF_ENUMERATE_SHOP = 165,
|
||||
MSG_MHF_GET_EXTRA_INFO = 166,
|
||||
MSG_MHF_UPDATE_INTERIOR = 167,
|
||||
MSG_MHF_ENUMERATE_HOUSE = 168,
|
||||
MSG_MHF_UPDATE_HOUSE = 169,
|
||||
MSG_MHF_LOAD_HOUSE = 170,
|
||||
MSG_MHF_OPERATE_WAREHOUSE = 171,
|
||||
MSG_MHF_ENUMERATE_WAREHOUSE = 172,
|
||||
MSG_MHF_UPDATE_WAREHOUSE = 173,
|
||||
MSG_MHF_ACQUIRE_TITLE = 174,
|
||||
MSG_MHF_ENUMERATE_TITLE = 175,
|
||||
MSG_MHF_ENUMERATE_GUILD_ITEM = 176,
|
||||
MSG_MHF_UPDATE_GUILD_ITEM = 177,
|
||||
MSG_MHF_ENUMERATE_UNION_ITEM = 178,
|
||||
MSG_MHF_UPDATE_UNION_ITEM = 179,
|
||||
MSG_MHF_CREATE_JOINT = 180,
|
||||
MSG_MHF_OPERATE_JOINT = 181,
|
||||
MSG_MHF_INFO_JOINT = 182,
|
||||
MSG_MHF_UPDATE_GUILD_ICON = 183,
|
||||
MSG_MHF_INFO_FESTA = 184,
|
||||
MSG_MHF_ENTRY_FESTA = 185,
|
||||
MSG_MHF_CHARGE_FESTA = 186,
|
||||
MSG_MHF_ACQUIRE_FESTA = 187,
|
||||
MSG_MHF_STATE_FESTA_U = 188,
|
||||
MSG_MHF_STATE_FESTA_G = 189,
|
||||
MSG_MHF_ENUMERATE_FESTA_MEMBER = 190,
|
||||
MSG_MHF_VOTE_FESTA = 191,
|
||||
MSG_MHF_ACQUIRE_CAFE_ITEM = 192,
|
||||
MSG_MHF_UPDATE_CAFEPOINT = 193,
|
||||
MSG_MHF_CHECK_DAILY_CAFEPOINT = 194,
|
||||
MSG_MHF_GET_COG_INFO = 195,
|
||||
MSG_MHF_CHECK_MONTHLY_ITEM = 196,
|
||||
MSG_MHF_ACQUIRE_MONTHLY_ITEM = 197,
|
||||
MSG_MHF_CHECK_WEEKLY_STAMP = 198,
|
||||
MSG_MHF_EXCHANGE_WEEKLY_STAMP = 199,
|
||||
MSG_MHF_CREATE_MERCENARY = 200,
|
||||
MSG_MHF_SAVE_MERCENARY = 201,
|
||||
MSG_MHF_READ_MERCENARY_W = 202,
|
||||
MSG_MHF_READ_MERCENARY_M = 203,
|
||||
MSG_MHF_CONTRACT_MERCENARY = 204,
|
||||
MSG_MHF_ENUMERATE_MERCENARY_LOG = 205,
|
||||
MSG_MHF_ENUMERATE_GUACOT = 206,
|
||||
MSG_MHF_UPDATE_GUACOT = 207,
|
||||
MSG_MHF_INFO_TOURNAMENT = 208,
|
||||
MSG_MHF_ENTRY_TOURNAMENT = 209,
|
||||
MSG_MHF_ENTER_TOURNAMENT_QUEST = 210,
|
||||
MSG_MHF_ACQUIRE_TOURNAMENT = 211,
|
||||
MSG_MHF_GET_ACHIEVEMENT = 212,
|
||||
MSG_MHF_RESET_ACHIEVEMENT = 213,
|
||||
MSG_MHF_ADD_ACHIEVEMENT = 214,
|
||||
MSG_MHF_PAYMENT_ACHIEVEMENT = 215,
|
||||
MSG_MHF_DISPLAYED_ACHIEVEMENT = 216,
|
||||
MSG_MHF_INFO_SCENARIO_COUNTER = 217,
|
||||
MSG_MHF_SAVE_SCENARIO_DATA = 218,
|
||||
MSG_MHF_LOAD_SCENARIO_DATA = 219,
|
||||
MSG_MHF_GET_BBS_SNS_STATUS = 220,
|
||||
MSG_MHF_APPLY_BBS_ARTICLE = 221,
|
||||
MSG_MHF_GET_ETC_POINTS = 222,
|
||||
MSG_MHF_UPDATE_ETC_POINT = 223,
|
||||
MSG_MHF_GET_MYHOUSE_INFO = 224,
|
||||
MSG_MHF_UPDATE_MYHOUSE_INFO = 225,
|
||||
MSG_MHF_GET_WEEKLY_SCHEDULE = 226,
|
||||
MSG_MHF_ENUMERATE_INV_GUILD = 227,
|
||||
MSG_MHF_OPERATION_INV_GUILD = 228,
|
||||
MSG_MHF_STAMPCARD_STAMP = 229,
|
||||
MSG_MHF_STAMPCARD_PRIZE = 230,
|
||||
MSG_MHF_UNRESERVE_SRG = 231,
|
||||
MSG_MHF_LOAD_PLATE_DATA = 232,
|
||||
MSG_MHF_SAVE_PLATE_DATA = 233,
|
||||
MSG_MHF_LOAD_PLATE_BOX = 234,
|
||||
MSG_MHF_SAVE_PLATE_BOX = 235,
|
||||
MSG_MHF_READ_GUILDCARD = 236,
|
||||
MSG_MHF_UPDATE_GUILDCARD = 237,
|
||||
MSG_MHF_READ_BEAT_LEVEL = 238,
|
||||
MSG_MHF_UPDATE_BEAT_LEVEL = 239,
|
||||
MSG_MHF_READ_BEAT_LEVEL_ALL_RANKING = 240,
|
||||
MSG_MHF_READ_BEAT_LEVEL_MY_RANKING = 241,
|
||||
MSG_MHF_READ_LAST_WEEK_BEAT_RANKING = 242,
|
||||
MSG_MHF_ACCEPT_READ_REWARD = 243,
|
||||
MSG_MHF_GET_ADDITIONAL_BEAT_REWARD = 244,
|
||||
MSG_MHF_GET_FIXED_SEIBATU_RANKING_TABLE = 245,
|
||||
MSG_MHF_GET_BBS_USER_STATUS = 246,
|
||||
MSG_MHF_KICK_EXPORT_FORCE = 247,
|
||||
MSG_MHF_GET_BREAK_SEIBATU_LEVEL_REWARD = 248,
|
||||
MSG_MHF_GET_WEEKLY_SEIBATU_RANKING_REWARD = 249,
|
||||
MSG_MHF_GET_EARTH_STATUS = 250,
|
||||
MSG_MHF_LOAD_PARTNER = 251,
|
||||
MSG_MHF_SAVE_PARTNER = 252,
|
||||
MSG_MHF_GET_GUILD_MISSION_LIST = 253,
|
||||
MSG_MHF_GET_GUILD_MISSION_RECORD = 254,
|
||||
MSG_MHF_ADD_GUILD_MISSION_COUNT = 255,
|
||||
MSG_MHF_SET_GUILD_MISSION_TARGET = 256,
|
||||
MSG_MHF_CANCEL_GUILD_MISSION_TARGET = 257,
|
||||
MSG_MHF_LOAD_OTOMO_AIROU = 258,
|
||||
MSG_MHF_SAVE_OTOMO_AIROU = 259,
|
||||
MSG_MHF_ENUMERATE_GUILD_TRESURE = 260,
|
||||
MSG_MHF_ENUMERATE_AIROULIST = 261,
|
||||
MSG_MHF_REGIST_GUILD_TRESURE = 262,
|
||||
MSG_MHF_ACQUIRE_GUILD_TRESURE = 263,
|
||||
MSG_MHF_OPERATE_GUILD_TRESURE_REPORT = 264,
|
||||
MSG_MHF_GET_GUILD_TRESURE_SOUVENIR = 265,
|
||||
MSG_MHF_ACQUIRE_GUILD_TRESURE_SOUVENIR = 266,
|
||||
MSG_MHF_ENUMERATE_FESTA_INTERMEDIATE_PRIZE = 267,
|
||||
MSG_MHF_ACQUIRE_FESTA_INTERMEDIATE_PRIZE = 268,
|
||||
MSG_MHF_LOAD_DECO_MYSET = 269,
|
||||
MSG_MHF_SAVE_DECO_MYSET = 270,
|
||||
MSG_MHF_reserve010F = 271,
|
||||
MSG_MHF_LOAD_GUILD_COOKING = 272,
|
||||
MSG_MHF_REGIST_GUILD_COOKING = 273,
|
||||
MSG_MHF_LOAD_GUILD_ADVENTURE = 274,
|
||||
MSG_MHF_REGIST_GUILD_ADVENTURE = 275,
|
||||
MSG_MHF_ACQUIRE_GUILD_ADVENTURE = 276,
|
||||
MSG_MHF_CHARGE_GUILD_ADVENTURE = 277,
|
||||
MSG_MHF_LOAD_LEGEND_DISPATCH = 278,
|
||||
MSG_MHF_LOAD_HUNTER_NAVI = 279,
|
||||
MSG_MHF_SAVE_HUNTER_NAVI = 280,
|
||||
MSG_MHF_REGIST_SPABI_TIME = 281,
|
||||
MSG_MHF_GET_GUILD_WEEKLY_BONUS_MASTER = 282,
|
||||
MSG_MHF_GET_GUILD_WEEKLY_BONUS_ACTIVE_COUNT = 283,
|
||||
MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER = 284,
|
||||
MSG_MHF_GET_TOWER_INFO = 285,
|
||||
MSG_MHF_POST_TOWER_INFO = 286,
|
||||
MSG_MHF_GET_GEM_INFO = 287,
|
||||
MSG_MHF_POST_GEM_INFO = 288,
|
||||
MSG_MHF_GET_EARTH_VALUE = 289,
|
||||
MSG_MHF_DEBUG_POST_VALUE = 290,
|
||||
MSG_MHF_GET_PAPER_DATA = 291,
|
||||
MSG_MHF_GET_NOTICE = 292,
|
||||
MSG_MHF_POST_NOTICE = 293,
|
||||
MSG_MHF_GET_BOOST_TIME = 294,
|
||||
MSG_MHF_POST_BOOST_TIME = 295,
|
||||
MSG_MHF_GET_BOOST_TIME_LIMIT = 296,
|
||||
MSG_MHF_POST_BOOST_TIME_LIMIT = 297,
|
||||
MSG_MHF_ENUMERATE_FESTA_PERSONAL_PRIZE = 298,
|
||||
MSG_MHF_ACQUIRE_FESTA_PERSONAL_PRIZE = 299,
|
||||
MSG_MHF_GET_RAND_FROM_TABLE = 300,
|
||||
MSG_MHF_GET_CAFE_DURATION = 301,
|
||||
MSG_MHF_GET_CAFE_DURATION_BONUS_INFO = 302,
|
||||
MSG_MHF_RECEIVE_CAFE_DURATION_BONUS = 303,
|
||||
MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED = 304,
|
||||
MSG_MHF_GET_GACHA_POINT = 305,
|
||||
MSG_MHF_USE_GACHA_POINT = 306,
|
||||
MSG_MHF_EXCHANGE_FPOINT_2_ITEM = 307,
|
||||
MSG_MHF_EXCHANGE_ITEM_2_FPOINT = 308,
|
||||
MSG_MHF_GET_FPOINT_EXCHANGE_LIST = 309,
|
||||
MSG_MHF_PLAY_STEPUP_GACHA = 310,
|
||||
MSG_MHF_RECEIVE_GACHA_ITEM = 311,
|
||||
MSG_MHF_GET_STEPUP_STATUS = 312,
|
||||
MSG_MHF_PLAY_FREE_GACHA = 313,
|
||||
MSG_MHF_GET_TINY_BIN = 314,
|
||||
MSG_MHF_POST_TINY_BIN = 315,
|
||||
MSG_MHF_GET_SENYU_DAILY_COUNT = 316,
|
||||
MSG_MHF_GET_GUILD_TARGET_MEMBER_NUM = 317,
|
||||
MSG_MHF_GET_BOOST_RIGHT = 318,
|
||||
MSG_MHF_START_BOOST_TIME = 319,
|
||||
MSG_MHF_POST_BOOST_TIME_QUEST_RETURN = 320,
|
||||
MSG_MHF_GET_BOX_GACHA_INFO = 321,
|
||||
MSG_MHF_PLAY_BOX_GACHA = 322,
|
||||
MSG_MHF_RESET_BOX_GACHA_INFO = 323,
|
||||
MSG_MHF_GET_SEIBATTLE = 324,
|
||||
MSG_MHF_POST_SEIBATTLE = 325,
|
||||
MSG_MHF_GET_RYOUDAMA = 326,
|
||||
MSG_MHF_POST_RYOUDAMA = 327,
|
||||
MSG_MHF_GET_TENROUIRAI = 328,
|
||||
MSG_MHF_POST_TENROUIRAI = 329,
|
||||
MSG_MHF_POST_GUILD_SCOUT = 330,
|
||||
MSG_MHF_CANCEL_GUILD_SCOUT = 331,
|
||||
MSG_MHF_ANSWER_GUILD_SCOUT = 332,
|
||||
MSG_MHF_GET_GUILD_SCOUT_LIST = 333,
|
||||
MSG_MHF_GET_GUILD_MANAGE_RIGHT = 334,
|
||||
MSG_MHF_SET_GUILD_MANAGE_RIGHT = 335,
|
||||
MSG_MHF_PLAY_NORMAL_GACHA = 336,
|
||||
MSG_MHF_GET_DAILY_MISSION_MASTER = 337,
|
||||
MSG_MHF_GET_DAILY_MISSION_PERSONAL = 338,
|
||||
MSG_MHF_SET_DAILY_MISSION_PERSONAL = 339,
|
||||
MSG_MHF_GET_GACHA_PLAY_HISTORY = 340,
|
||||
MSG_MHF_GET_REJECT_GUILD_SCOUT = 341,
|
||||
MSG_MHF_SET_REJECT_GUILD_SCOUT = 342,
|
||||
MSG_MHF_GET_CA_ACHIEVEMENT_HIST = 343,
|
||||
MSG_MHF_SET_CA_ACHIEVEMENT_HIST = 344,
|
||||
MSG_MHF_GET_KEEP_LOGIN_BOOST_STATUS = 345,
|
||||
MSG_MHF_USE_KEEP_LOGIN_BOOST = 346,
|
||||
MSG_MHF_GET_UD_SCHEDULE = 347,
|
||||
MSG_MHF_GET_UD_INFO = 348,
|
||||
MSG_MHF_GET_KIJU_INFO = 349,
|
||||
MSG_MHF_SET_KIJU = 350,
|
||||
MSG_MHF_ADD_UD_POINT = 351,
|
||||
MSG_MHF_GET_UD_MY_POINT = 352,
|
||||
MSG_MHF_GET_UD_TOTAL_POINT_INFO = 353,
|
||||
MSG_MHF_GET_UD_BONUS_QUEST_INFO = 354,
|
||||
MSG_MHF_GET_UD_SELECTED_COLOR_INFO = 355,
|
||||
MSG_MHF_GET_UD_MONSTER_POINT = 356,
|
||||
MSG_MHF_GET_UD_DAILY_PRESENT_LIST = 357,
|
||||
MSG_MHF_GET_UD_NORMA_PRESENT_LIST = 358,
|
||||
MSG_MHF_GET_UD_RANKING_REWARD_LIST = 359,
|
||||
MSG_MHF_ACQUIRE_UD_ITEM = 360,
|
||||
MSG_MHF_GET_REWARD_SONG = 361,
|
||||
MSG_MHF_USE_REWARD_SONG = 362,
|
||||
MSG_MHF_ADD_REWARD_SONG_COUNT = 363,
|
||||
MSG_MHF_GET_UD_RANKING = 364,
|
||||
MSG_MHF_GET_UD_MY_RANKING = 365,
|
||||
MSG_MHF_ACQUIRE_MONTHLY_REWARD = 366,
|
||||
MSG_MHF_GET_UD_GUILD_MAP_INFO = 367,
|
||||
MSG_MHF_GENERATE_UD_GUILD_MAP = 368,
|
||||
MSG_MHF_GET_UD_TACTICS_POINT = 369,
|
||||
MSG_MHF_ADD_UD_TACTICS_POINT = 370,
|
||||
MSG_MHF_GET_UD_TACTICS_RANKING = 371,
|
||||
MSG_MHF_GET_UD_TACTICS_REWARD_LIST = 372,
|
||||
MSG_MHF_GET_UD_TACTICS_LOG = 373,
|
||||
MSG_MHF_GET_EQUIP_SKIN_HIST = 374,
|
||||
MSG_MHF_UPDATE_EQUIP_SKIN_HIST = 375,
|
||||
MSG_MHF_GET_UD_TACTICS_FOLLOWER = 376,
|
||||
MSG_MHF_SET_UD_TACTICS_FOLLOWER = 377,
|
||||
MSG_MHF_GET_UD_SHOP_COIN = 378,
|
||||
MSG_MHF_USE_UD_SHOP_COIN = 379,
|
||||
MSG_MHF_GET_ENHANCED_MINIDATA = 380,
|
||||
MSG_MHF_SET_ENHANCED_MINIDATA = 381,
|
||||
MSG_MHF_SEX_CHANGER = 382,
|
||||
MSG_MHF_GET_LOBBY_CROWD = 383,
|
||||
MSG_SYS_reserve180 = 384,
|
||||
MSG_MHF_GUILD_HUNTDATA = 385,
|
||||
MSG_MHF_ADD_KOURYOU_POINT = 386,
|
||||
MSG_MHF_GET_KOURYOU_POINT = 387,
|
||||
MSG_MHF_EXCHANGE_KOURYOU_POINT = 388,
|
||||
MSG_MHF_GET_UD_TACTICS_BONUS_QUEST = 389,
|
||||
MSG_MHF_GET_UD_TACTICS_FIRST_QUEST_BONUS = 390,
|
||||
MSG_MHF_GET_UD_TACTICS_REMAINING_POINT = 391,
|
||||
MSG_SYS_reserve188 = 392,
|
||||
MSG_MHF_LOAD_PLATE_MYSET = 393,
|
||||
MSG_MHF_SAVE_PLATE_MYSET = 394,
|
||||
MSG_SYS_reserve18B = 395,
|
||||
MSG_MHF_GET_RESTRICTION_EVENT = 396,
|
||||
MSG_MHF_SET_RESTRICTION_EVENT = 397,
|
||||
MSG_SYS_reserve18E = 398,
|
||||
MSG_SYS_reserve18F = 399,
|
||||
MSG_MHF_GET_TREND_WEAPON = 400,
|
||||
MSG_MHF_UPDATE_USE_TREND_WEAPON_LOG = 401,
|
||||
MSG_SYS_reserve192 = 402,
|
||||
MSG_SYS_reserve193 = 403,
|
||||
MSG_SYS_reserve194 = 404,
|
||||
MSG_MHF_SAVE_RENGOKU_DATA = 405,
|
||||
MSG_MHF_LOAD_RENGOKU_DATA = 406,
|
||||
MSG_MHF_GET_RENGOKU_BINARY = 407,
|
||||
MSG_MHF_ENUMERATE_RENGOKU_RANKING = 408,
|
||||
MSG_MHF_GET_RENGOKU_RANKING_RANK = 409,
|
||||
MSG_MHF_ACQUIRE_EXCHANGE_SHOP = 410,
|
||||
MSG_SYS_reserve19B = 411,
|
||||
MSG_MHF_SAVE_MEZFES_DATA = 412,
|
||||
MSG_MHF_LOAD_MEZFES_DATA = 413,
|
||||
MSG_SYS_reserve19E = 414,
|
||||
MSG_SYS_reserve19F = 415,
|
||||
MSG_MHF_UPDATE_FORCE_GUILD_RANK = 416,
|
||||
MSG_MHF_RESET_TITLE = 417,
|
||||
MSG_SYS_reserve202 = 418,
|
||||
MSG_SYS_reserve203 = 419,
|
||||
MSG_SYS_reserve204 = 420,
|
||||
MSG_SYS_reserve205 = 421,
|
||||
MSG_SYS_reserve206 = 422,
|
||||
MSG_SYS_reserve207 = 423,
|
||||
MSG_SYS_reserve208 = 424,
|
||||
MSG_SYS_reserve209 = 425,
|
||||
MSG_SYS_reserve20A = 426,
|
||||
MSG_SYS_reserve20B = 427,
|
||||
MSG_SYS_reserve20C = 428,
|
||||
MSG_SYS_reserve20D = 429,
|
||||
MSG_SYS_reserve20E = 430,
|
||||
MSG_SYS_reserve20F = 431,
|
||||
}
|
||||
}
|
48
Mhf.Server/Packet/PacketResponse.cs
Normal file
48
Mhf.Server/Packet/PacketResponse.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using Arrowgene.Services.Buffers;
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public abstract class PacketResponse
|
||||
{
|
||||
private readonly List<ISender> _receiver;
|
||||
private MhfPacket _packet;
|
||||
|
||||
public PacketResponse(ushort id)
|
||||
{
|
||||
_receiver = new List<ISender>();
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public List<ISender> Receiver => new List<ISender>(_receiver);
|
||||
public ushort Id { get; }
|
||||
|
||||
protected abstract IBuffer ToBuffer();
|
||||
|
||||
public MhfPacket ToPacket()
|
||||
{
|
||||
if (_packet == null)
|
||||
{
|
||||
_packet = new MhfPacket(Id, ToBuffer());
|
||||
}
|
||||
|
||||
return _packet;
|
||||
}
|
||||
|
||||
public void AddReceiver(params ISender[] receiver)
|
||||
{
|
||||
_receiver.AddRange(receiver);
|
||||
}
|
||||
|
||||
public void AddReceiver(IEnumerable<ISender> receiver)
|
||||
{
|
||||
_receiver.AddRange(receiver);
|
||||
}
|
||||
|
||||
public void CleatReceivers()
|
||||
{
|
||||
_receiver.Clear();
|
||||
}
|
||||
}
|
||||
}
|
92
Mhf.Server/Packet/PacketRouter.cs
Normal file
92
Mhf.Server/Packet/PacketRouter.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using Arrowgene.Services.Buffers;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Logging;
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Packet
|
||||
{
|
||||
public class PacketRouter
|
||||
{
|
||||
private readonly MhfLogger _logger;
|
||||
|
||||
public PacketRouter()
|
||||
{
|
||||
_logger = LogProvider.Logger<MhfLogger>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a packet to a client.
|
||||
/// </summary>
|
||||
public void Send(ISender client, MhfPacket packet)
|
||||
{
|
||||
client.Send(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a packet to a client.
|
||||
/// </summary>
|
||||
public void Send(ISender client, ushort id, IBuffer data)
|
||||
{
|
||||
MhfPacket packet = new MhfPacket(id, data);
|
||||
Send(client, packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a packet to multiple clients.
|
||||
/// </summary>
|
||||
/// <param name="excepts">clients to exclude</param>
|
||||
public void Send(List<ISender> clients, MhfPacket packet, params ISender[] excepts)
|
||||
{
|
||||
clients = GetClients(clients, excepts);
|
||||
foreach (MhfClient client in clients)
|
||||
{
|
||||
Send(client, packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a packet to multiple clients.
|
||||
/// </summary>
|
||||
/// <param name="excepts">clients to exclude</param>
|
||||
public void Send(List<ISender> clients, ushort id, IBuffer data, params ISender[] excepts)
|
||||
{
|
||||
Send(clients, new MhfPacket(id, data), excepts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a specific packet response.
|
||||
/// </summary>
|
||||
public void Send(PacketResponse response)
|
||||
{
|
||||
foreach (ISender client in response.Receiver)
|
||||
{
|
||||
Send(client, response.ToPacket());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a specific packet response.
|
||||
/// </summary>
|
||||
public void Send(PacketResponse response, params ISender[] clients)
|
||||
{
|
||||
response.AddReceiver(clients);
|
||||
Send(response);
|
||||
}
|
||||
|
||||
private List<ISender> GetClients(List<ISender> clients, params ISender[] excepts)
|
||||
{
|
||||
if (excepts.Length == 0)
|
||||
{
|
||||
return clients;
|
||||
}
|
||||
|
||||
foreach (ISender except in excepts)
|
||||
{
|
||||
clients.Remove(except);
|
||||
}
|
||||
|
||||
return clients;
|
||||
}
|
||||
}
|
||||
}
|
27
Mhf.Server/PacketHandler/MsgHeadHandler.cs
Normal file
27
Mhf.Server/PacketHandler/MsgHeadHandler.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Mhf.Server.Model;
|
||||
using Mhf.Server.Packet;
|
||||
using Mhf.Server.PacketResponses;
|
||||
|
||||
namespace Mhf.Server.PacketHandler
|
||||
{
|
||||
public class MsgHeadHandler : ConnectionHandler
|
||||
{
|
||||
public MsgHeadHandler(MhfServer server) : base(server)
|
||||
{
|
||||
}
|
||||
|
||||
public override ushort Id => (ushort) PacketId.MSG_HEAD;
|
||||
|
||||
public override void Handle(MhfConnection connection, MhfPacket packet)
|
||||
{
|
||||
string keySign = packet.Data.ReadCString();
|
||||
string userId = packet.Data.ReadCString();
|
||||
string sessionKey = packet.Data.ReadCString();
|
||||
|
||||
Logger.Info($"Auth Request: KeySign:{keySign} UserId:{userId} SessionKey:{sessionKey}");
|
||||
|
||||
MsgHeadResponse response = new MsgHeadResponse();
|
||||
Router.Send(response, connection);
|
||||
}
|
||||
};
|
||||
}
|
42
Mhf.Server/PacketResponses/MsgHeadResponse.cs
Normal file
42
Mhf.Server/PacketResponses/MsgHeadResponse.cs
Normal file
File diff suppressed because one or more lines are too long
363
Mhf.Server/QueueConsumer.cs
Normal file
363
Mhf.Server/QueueConsumer.cs
Normal file
@ -0,0 +1,363 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Arrowgene.Services;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Arrowgene.Services.Networking.Tcp;
|
||||
using Arrowgene.Services.Networking.Tcp.Consumer;
|
||||
using Arrowgene.Services.Networking.Tcp.Consumer.BlockingQueueConsumption;
|
||||
using Arrowgene.Services.Networking.Tcp.Server.AsyncEvent;
|
||||
using Mhf.Server.Logging;
|
||||
using Mhf.Server.Model;
|
||||
using Mhf.Server.Packet;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Server
|
||||
{
|
||||
public class QueueConsumer : IConsumer
|
||||
{
|
||||
public const int NoExpectedSize = -1;
|
||||
|
||||
private readonly BlockingCollection<ClientEvent>[] _queues;
|
||||
private readonly Thread[] _threads;
|
||||
private readonly Dictionary<int, IClientHandler> _clientHandlers;
|
||||
private readonly Dictionary<int, IConnectionHandler> _connectionHandlers;
|
||||
private readonly Dictionary<ITcpSocket, MhfConnection> _connections;
|
||||
private readonly MhfLogger _logger;
|
||||
private readonly object _lock;
|
||||
private readonly int _maxUnitOfOrder;
|
||||
private MhfSetting _setting;
|
||||
private volatile bool _isRunning;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public int HandlersCount => _clientHandlers.Count;
|
||||
|
||||
public Action<MhfConnection> ClientDisconnected;
|
||||
public Action<MhfConnection> ClientConnected;
|
||||
public Action Started;
|
||||
public Action Stopped;
|
||||
|
||||
public QueueConsumer(MhfSetting setting, AsyncEventSettings socketSetting)
|
||||
{
|
||||
_setting = setting;
|
||||
_logger = LogProvider.Logger<MhfLogger>(this);
|
||||
_maxUnitOfOrder = socketSetting.MaxUnitOfOrder;
|
||||
_queues = new BlockingCollection<ClientEvent>[_maxUnitOfOrder];
|
||||
_threads = new Thread[_maxUnitOfOrder];
|
||||
_lock = new object();
|
||||
_clientHandlers = new Dictionary<int, IClientHandler>();
|
||||
_connectionHandlers = new Dictionary<int, IConnectionHandler>();
|
||||
_connections = new Dictionary<ITcpSocket, MhfConnection>();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_clientHandlers.Clear();
|
||||
_connectionHandlers.Clear();
|
||||
}
|
||||
|
||||
public void AddHandler(IClientHandler clientHandler, bool overwrite = false)
|
||||
{
|
||||
if (overwrite)
|
||||
{
|
||||
if (_clientHandlers.ContainsKey(clientHandler.Id))
|
||||
{
|
||||
_clientHandlers[clientHandler.Id] = clientHandler;
|
||||
}
|
||||
else
|
||||
{
|
||||
_clientHandlers.Add(clientHandler.Id, clientHandler);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_clientHandlers.ContainsKey(clientHandler.Id))
|
||||
{
|
||||
_logger.Error($"ClientHandlerId: {clientHandler.Id} already exists");
|
||||
}
|
||||
else
|
||||
{
|
||||
_clientHandlers.Add(clientHandler.Id, clientHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddHandler(IConnectionHandler connectionHandler, bool overwrite = false)
|
||||
{
|
||||
if (overwrite)
|
||||
{
|
||||
if (_connectionHandlers.ContainsKey(connectionHandler.Id))
|
||||
{
|
||||
_connectionHandlers[connectionHandler.Id] = connectionHandler;
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionHandlers.Add(connectionHandler.Id, connectionHandler);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_connectionHandlers.ContainsKey(connectionHandler.Id))
|
||||
{
|
||||
_logger.Error($"ConnectionHandlerId: {connectionHandler.Id} already exists");
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionHandlers.Add(connectionHandler.Id, connectionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleReceived(ITcpSocket socket, byte[] data)
|
||||
{
|
||||
if (!socket.IsAlive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MhfConnection connection;
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_connections.ContainsKey(socket))
|
||||
{
|
||||
_logger.Error(socket, $"Client does not exist in lookup");
|
||||
return;
|
||||
}
|
||||
|
||||
connection = _connections[socket];
|
||||
}
|
||||
|
||||
List<MhfPacket> packets = connection.Receive(data);
|
||||
foreach (MhfPacket packet in packets)
|
||||
{
|
||||
MhfClient client = connection.Client;
|
||||
if (client != null)
|
||||
{
|
||||
HandleReceived_Client(client, packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleReceived_Connection(connection, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleReceived_Connection(MhfConnection connection, MhfPacket packet)
|
||||
{
|
||||
if (!_connectionHandlers.ContainsKey(packet.Id))
|
||||
{
|
||||
// _logger.LogUnknownIncomingPacket(connection, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
IConnectionHandler connectionHandler = _connectionHandlers[packet.Id];
|
||||
if (connectionHandler.ExpectedSize != NoExpectedSize && packet.Data.Size < connectionHandler.ExpectedSize)
|
||||
{
|
||||
_logger.Error(connection,
|
||||
$"Ignoring Packed (Id:{packet.Id}) is smaller ({packet.Data.Size}) than expected ({connectionHandler.ExpectedSize})");
|
||||
return;
|
||||
}
|
||||
|
||||
// _logger.LogIncomingPacket(connection, packet);
|
||||
packet.Data.SetPositionStart();
|
||||
try
|
||||
{
|
||||
connectionHandler.Handle(connection, packet);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(connection, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleReceived_Client(MhfClient client, MhfPacket packet)
|
||||
{
|
||||
if (!_clientHandlers.ContainsKey(packet.Id))
|
||||
{
|
||||
//_logger.LogUnknownIncomingPacket(client, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
IClientHandler clientHandler = _clientHandlers[packet.Id];
|
||||
if (clientHandler.ExpectedSize != NoExpectedSize && packet.Data.Size < clientHandler.ExpectedSize)
|
||||
{
|
||||
_logger.Error(client,
|
||||
$"Ignoring Packed (Id:{packet.Id}) is smaller ({packet.Data.Size}) than expected ({clientHandler.ExpectedSize})");
|
||||
return;
|
||||
}
|
||||
|
||||
// _logger.LogIncomingPacket(client, packet);
|
||||
packet.Data.SetPositionStart();
|
||||
try
|
||||
{
|
||||
clientHandler.Handle(client, packet);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(client, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDisconnected(ITcpSocket socket)
|
||||
{
|
||||
MhfConnection connection;
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_connections.ContainsKey(socket))
|
||||
{
|
||||
_logger.Error(socket, $"Disconnected client does not exist in lookup");
|
||||
return;
|
||||
}
|
||||
|
||||
connection = _connections[socket];
|
||||
_connections.Remove(socket);
|
||||
_logger.Debug($"Clients Count: {_connections.Count}");
|
||||
}
|
||||
|
||||
Action<MhfConnection> onClientDisconnected = ClientDisconnected;
|
||||
if (onClientDisconnected != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
onClientDisconnected.Invoke(connection);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(connection, ex);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Info(connection, $"Client disconnected");
|
||||
}
|
||||
|
||||
private void HandleConnected(ITcpSocket socket)
|
||||
{
|
||||
MhfConnection connection = new MhfConnection(socket, new PacketFactory(_setting));
|
||||
lock (_lock)
|
||||
{
|
||||
_connections.Add(socket, connection);
|
||||
_logger.Debug($"Clients Count: {_connections.Count}");
|
||||
}
|
||||
|
||||
Action<MhfConnection> onClientConnected = ClientConnected;
|
||||
if (onClientConnected != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
onClientConnected.Invoke(connection);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(connection, ex);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Info(connection, $"Client connected");
|
||||
}
|
||||
|
||||
private void Consume(int unitOfOrder)
|
||||
{
|
||||
while (_isRunning)
|
||||
{
|
||||
ClientEvent clientEvent;
|
||||
try
|
||||
{
|
||||
clientEvent = _queues[unitOfOrder].Take(_cancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (clientEvent.ClientEventType)
|
||||
{
|
||||
case ClientEventType.ReceivedData:
|
||||
HandleReceived(clientEvent.Socket, clientEvent.Data);
|
||||
break;
|
||||
case ClientEventType.Connected:
|
||||
HandleConnected(clientEvent.Socket);
|
||||
break;
|
||||
case ClientEventType.Disconnected:
|
||||
HandleDisconnected(clientEvent.Socket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IConsumer.OnStart()
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
_logger.Error($"Consumer already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_isRunning = true;
|
||||
for (int i = 0; i < _maxUnitOfOrder; i++)
|
||||
{
|
||||
int uuo = i;
|
||||
_queues[i] = new BlockingCollection<ClientEvent>();
|
||||
_threads[i] = new Thread(() => Consume(uuo));
|
||||
_threads[i].Name = $"Consumer: {i}";
|
||||
_logger.Info($"Starting Consumer: {i}");
|
||||
_threads[i].Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnStarted()
|
||||
{
|
||||
Action started = Started;
|
||||
if (started != null)
|
||||
{
|
||||
started.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
void IConsumer.OnReceivedData(ITcpSocket socket, byte[] data)
|
||||
{
|
||||
_queues[socket.UnitOfOrder].Add(new ClientEvent(socket, ClientEventType.ReceivedData, data));
|
||||
}
|
||||
|
||||
void IConsumer.OnClientDisconnected(ITcpSocket socket)
|
||||
{
|
||||
_queues[socket.UnitOfOrder].Add(new ClientEvent(socket, ClientEventType.Disconnected));
|
||||
}
|
||||
|
||||
void IConsumer.OnClientConnected(ITcpSocket socket)
|
||||
{
|
||||
_queues[socket.UnitOfOrder].Add(new ClientEvent(socket, ClientEventType.Connected));
|
||||
}
|
||||
|
||||
void IConsumer.OnStop()
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
_logger.Error($"Consumer already stopped.");
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
_cancellationTokenSource.Cancel();
|
||||
for (int i = 0; i < _maxUnitOfOrder; i++)
|
||||
{
|
||||
Thread consumerThread = _threads[i];
|
||||
_logger.Info($"Shutting Consumer: {i} down...");
|
||||
Service.JoinThread(consumerThread, 10000, _logger);
|
||||
_logger.Info($"Consumer: {i} ended.");
|
||||
_threads[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnStopped()
|
||||
{
|
||||
Action stopped = Stopped;
|
||||
if (stopped != null)
|
||||
{
|
||||
stopped.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
68
Mhf.Server/Setting/DatabaseSetting.cs
Normal file
68
Mhf.Server/Setting/DatabaseSetting.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using Mhf.Server.Common;
|
||||
using Mhf.Server.Model;
|
||||
|
||||
namespace Mhf.Server.Setting
|
||||
{
|
||||
[DataContract]
|
||||
public class DatabaseSetting
|
||||
{
|
||||
public DatabaseSetting()
|
||||
{
|
||||
Type = DatabaseType.SQLite;
|
||||
SqLiteFolder = Path.Combine(Util.ExecutingDirectory(), "Files/Database");
|
||||
Host = "localhost";
|
||||
Port = 3306;
|
||||
Database = "Mhf";
|
||||
User = string.Empty;
|
||||
Password = string.Empty;
|
||||
|
||||
string envDbType = Environment.GetEnvironmentVariable("DB_TYPE");
|
||||
switch (envDbType)
|
||||
{
|
||||
case "sqlite":
|
||||
Type = DatabaseType.SQLite;
|
||||
break;
|
||||
}
|
||||
|
||||
string envDbUser = Environment.GetEnvironmentVariable("DB_USER");
|
||||
if (!string.IsNullOrEmpty(envDbUser))
|
||||
{
|
||||
User = envDbUser;
|
||||
}
|
||||
|
||||
string envDbPass = Environment.GetEnvironmentVariable("DB_PASS");
|
||||
if (!string.IsNullOrEmpty(envDbPass))
|
||||
{
|
||||
Password = envDbPass;
|
||||
}
|
||||
}
|
||||
|
||||
public DatabaseSetting(DatabaseSetting databaseSettings)
|
||||
{
|
||||
Type = databaseSettings.Type;
|
||||
SqLiteFolder = databaseSettings.SqLiteFolder;
|
||||
Host = databaseSettings.Host;
|
||||
Port = databaseSettings.Port;
|
||||
User = databaseSettings.User;
|
||||
Password = databaseSettings.Password;
|
||||
Database = databaseSettings.Database;
|
||||
}
|
||||
|
||||
[DataMember(Order = 0)] public DatabaseType Type { get; set; }
|
||||
|
||||
[DataMember(Order = 1)] public string SqLiteFolder { get; set; }
|
||||
|
||||
[DataMember(Order = 2)] public string Host { get; set; }
|
||||
|
||||
[DataMember(Order = 3)] public short Port { get; set; }
|
||||
|
||||
[DataMember(Order = 4)] public string User { get; set; }
|
||||
|
||||
[DataMember(Order = 5)] public string Password { get; set; }
|
||||
|
||||
[DataMember(Order = 6)] public string Database { get; set; }
|
||||
}
|
||||
}
|
105
Mhf.Server/Setting/MhfSetting.cs
Normal file
105
Mhf.Server/Setting/MhfSetting.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
using Arrowgene.Services.Networking.Tcp.Server.AsyncEvent;
|
||||
using Mhf.Server.Common;
|
||||
|
||||
namespace Mhf.Server.Setting
|
||||
{
|
||||
[DataContract]
|
||||
public class MhfSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Warning:
|
||||
/// Changing while having existing accounts requires to rehash all passwords.
|
||||
/// The number is log2, so adding +1 doubles the time it takes.
|
||||
/// https://wildlyinaccurate.com/bcrypt-choosing-a-work-factor/
|
||||
/// </summary>
|
||||
public const int BCryptWorkFactor = 10;
|
||||
|
||||
[IgnoreDataMember] public IPAddress ListenIpAddress { get; set; }
|
||||
|
||||
[DataMember(Name = "ListenIpAddress", Order = 0)]
|
||||
public string DataListenIpAddress
|
||||
{
|
||||
get => ListenIpAddress.ToString();
|
||||
set => ListenIpAddress = string.IsNullOrEmpty(value) ? null : IPAddress.Parse(value);
|
||||
}
|
||||
|
||||
[IgnoreDataMember] public IPAddress AuthServerIpAddress { get; set; }
|
||||
|
||||
[DataMember(Name = "AuthIpAddress", Order = 1)]
|
||||
public string DataAuthIpAddress
|
||||
{
|
||||
get => AuthServerIpAddress.ToString();
|
||||
set => AuthServerIpAddress = string.IsNullOrEmpty(value) ? null : IPAddress.Parse(value);
|
||||
}
|
||||
|
||||
[DataMember(Order = 2)] public ushort AuthServerPort { get; set; }
|
||||
|
||||
[IgnoreDataMember] public IPAddress LobbyServerIpAddress { get; set; }
|
||||
|
||||
[DataMember(Name = "LobbyIpAddress", Order = 3)]
|
||||
public string DataLobbyIpAddress
|
||||
{
|
||||
get => LobbyServerIpAddress.ToString();
|
||||
set => LobbyServerIpAddress = string.IsNullOrEmpty(value) ? null : IPAddress.Parse(value);
|
||||
}
|
||||
|
||||
[DataMember(Order = 4)] public ushort LobbyServerPort { get; set; }
|
||||
|
||||
[DataMember(Order = 10)] public bool NeedRegistration { get; set; }
|
||||
|
||||
[DataMember(Order = 20)] public int LogLevel { get; set; }
|
||||
|
||||
[DataMember(Order = 21)] public bool LogUnknownIncomingPackets { get; set; }
|
||||
|
||||
[DataMember(Order = 22)] public bool LogOutgoingPackets { get; set; }
|
||||
|
||||
[DataMember(Order = 23)] public bool LogIncomingPackets { get; set; }
|
||||
|
||||
[DataMember(Order = 40)] public string FilesFolder { get; set; }
|
||||
|
||||
[DataMember(Order = 60)] public WebSetting WebSetting { get; set; }
|
||||
[DataMember(Order = 70)] public DatabaseSetting DatabaseSetting { get; set; }
|
||||
|
||||
[DataMember(Order = 100)] public AsyncEventSettings ServerSocketSettings { get; set; }
|
||||
|
||||
public MhfSetting()
|
||||
{
|
||||
ListenIpAddress = IPAddress.Any;
|
||||
AuthServerIpAddress = IPAddress.Loopback;
|
||||
AuthServerPort = 53312;
|
||||
LobbyServerIpAddress = IPAddress.Loopback;
|
||||
LobbyServerPort = 53310;
|
||||
NeedRegistration = false;
|
||||
LogLevel = 0;
|
||||
LogUnknownIncomingPackets = true;
|
||||
LogOutgoingPackets = true;
|
||||
LogIncomingPackets = true;
|
||||
WebSetting = new WebSetting();
|
||||
DatabaseSetting = new DatabaseSetting();
|
||||
ServerSocketSettings = new AsyncEventSettings();
|
||||
ServerSocketSettings.MaxUnitOfOrder = 2;
|
||||
FilesFolder = Path.Combine(Util.ExecutingDirectory(), "Files");
|
||||
}
|
||||
|
||||
public MhfSetting(MhfSetting setting)
|
||||
{
|
||||
ListenIpAddress = setting.ListenIpAddress;
|
||||
AuthServerIpAddress = setting.AuthServerIpAddress;
|
||||
AuthServerPort = setting.AuthServerPort;
|
||||
LobbyServerIpAddress = setting.LobbyServerIpAddress;
|
||||
LobbyServerPort = setting.LobbyServerPort;
|
||||
NeedRegistration = setting.NeedRegistration;
|
||||
LogLevel = setting.LogLevel;
|
||||
LogUnknownIncomingPackets = setting.LogUnknownIncomingPackets;
|
||||
LogOutgoingPackets = setting.LogOutgoingPackets;
|
||||
LogIncomingPackets = setting.LogIncomingPackets;
|
||||
WebSetting = setting.WebSetting;
|
||||
FilesFolder = setting.FilesFolder;
|
||||
DatabaseSetting = new DatabaseSetting(setting.DatabaseSetting);
|
||||
ServerSocketSettings = new AsyncEventSettings(setting.ServerSocketSettings);
|
||||
}
|
||||
}
|
||||
}
|
53
Mhf.Server/Setting/SettingProvider.cs
Normal file
53
Mhf.Server/Setting/SettingProvider.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.IO;
|
||||
using Arrowgene.Services.Serialization;
|
||||
using Mhf.Server.Common;
|
||||
|
||||
namespace Mhf.Server.Setting
|
||||
{
|
||||
public class SettingProvider
|
||||
{
|
||||
private readonly string _directory;
|
||||
|
||||
|
||||
public SettingProvider(string directory = null)
|
||||
{
|
||||
if (Directory.Exists(directory))
|
||||
{
|
||||
_directory = directory;
|
||||
}
|
||||
else
|
||||
{
|
||||
_directory = Util.ExecutingDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetSettingsPath(string file)
|
||||
{
|
||||
return Path.Combine(_directory, file);
|
||||
}
|
||||
|
||||
public void Save<T>(T settings, string file)
|
||||
{
|
||||
string path = GetSettingsPath(file);
|
||||
string json = JsonSerializer.Serialize(settings);
|
||||
File.WriteAllText(path, json);
|
||||
}
|
||||
|
||||
public T Load<T>(string file)
|
||||
{
|
||||
T settings;
|
||||
string path = GetSettingsPath(file);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string json = File.ReadAllText(path);
|
||||
settings = JsonSerializer.Deserialize<T>(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings = default(T);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
37
Mhf.Server/Setting/WebSetting.cs
Normal file
37
Mhf.Server/Setting/WebSetting.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using Mhf.Server.Common;
|
||||
|
||||
namespace Mhf.Server.Setting
|
||||
{
|
||||
[DataContract]
|
||||
public class WebSetting
|
||||
{
|
||||
public WebSetting()
|
||||
{
|
||||
ServerHeader = null;
|
||||
WebFolder = Path.Combine(Util.ExecutingDirectory(), "Files/www");
|
||||
HttpPort = 80;
|
||||
HttpsEnabled = true;
|
||||
HttpsPort = 443;
|
||||
HttpsCertPath = Path.Combine(Util.ExecutingDirectory(), "Files/mhf.pfx");
|
||||
}
|
||||
|
||||
public WebSetting(WebSetting webSetting)
|
||||
{
|
||||
ServerHeader = webSetting.ServerHeader;
|
||||
WebFolder = webSetting.WebFolder;
|
||||
HttpPort = webSetting.HttpPort;
|
||||
HttpsEnabled = webSetting.HttpsEnabled;
|
||||
HttpsPort = webSetting.HttpsPort;
|
||||
HttpsCertPath = webSetting.HttpsCertPath;
|
||||
}
|
||||
|
||||
[DataMember(Order = 1)] public string ServerHeader { get; set; }
|
||||
[DataMember(Order = 2)] public short HttpPort { get; set; }
|
||||
[DataMember(Order = 3)] public bool HttpsEnabled { get; set; }
|
||||
[DataMember(Order = 4)] public short HttpsPort { get; set; }
|
||||
[DataMember(Order = 5)] public string HttpsCertPath { get; set; }
|
||||
[DataMember(Order = 6)] public string WebFolder { get; set; }
|
||||
}
|
||||
}
|
12
Mhf.Server/Web/Middleware/IWebMiddleware.cs
Normal file
12
Mhf.Server/Web/Middleware/IWebMiddleware.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Web.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a middleware
|
||||
/// </summary>
|
||||
public interface IWebMiddleware
|
||||
{
|
||||
Task<WebResponse> Handle(WebRequest request, WebMiddlewareDelegate next);
|
||||
}
|
||||
}
|
11
Mhf.Server/Web/Middleware/WebMiddleware.cs
Normal file
11
Mhf.Server/Web/Middleware/WebMiddleware.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
using Arrowgene.Services.Logging;
|
||||
|
||||
namespace Mhf.Server.Web.Middleware
|
||||
{
|
||||
public abstract class WebMiddleware : IWebMiddleware
|
||||
{
|
||||
protected ILogger Logger => LogProvider.Instance.GetLogger(this);
|
||||
public abstract Task<WebResponse> Handle(WebRequest request, WebMiddlewareDelegate next);
|
||||
}
|
||||
}
|
6
Mhf.Server/Web/Middleware/WebMiddlewareDelegate.cs
Normal file
6
Mhf.Server/Web/Middleware/WebMiddlewareDelegate.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Web.Middleware
|
||||
{
|
||||
public delegate Task<WebResponse> WebMiddlewareDelegate(WebRequest request);
|
||||
}
|
28
Mhf.Server/Web/Middleware/WebMiddlewareStack.cs
Normal file
28
Mhf.Server/Web/Middleware/WebMiddlewareStack.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Web.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of a middleware
|
||||
/// </summary>
|
||||
public class WebMiddlewareStack
|
||||
{
|
||||
private WebMiddlewareDelegate _webMiddlewareDelegate;
|
||||
|
||||
public WebMiddlewareStack(WebMiddlewareDelegate kernel)
|
||||
{
|
||||
_webMiddlewareDelegate = kernel;
|
||||
}
|
||||
|
||||
public Task<WebResponse> Start(WebRequest request)
|
||||
{
|
||||
return _webMiddlewareDelegate(request);
|
||||
}
|
||||
|
||||
public void Use(Func<WebMiddlewareDelegate, WebMiddlewareDelegate> middleware)
|
||||
{
|
||||
_webMiddlewareDelegate = middleware(_webMiddlewareDelegate);
|
||||
}
|
||||
}
|
||||
}
|
655
Mhf.Server/Web/MimeTypeMap.cs
Normal file
655
Mhf.Server/Web/MimeTypeMap.cs
Normal file
@ -0,0 +1,655 @@
|
||||
namespace Mhf.Server.Web
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class MimeTypeMap
|
||||
{
|
||||
private static readonly IDictionary<string, string> _mappings =
|
||||
new Dictionary<string, string>(BuildMappings());
|
||||
|
||||
private static IDictionary<string, string> BuildMappings()
|
||||
{
|
||||
var mappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// Monster Hunter Specific
|
||||
{".xml", "application/xml"},
|
||||
{".exe", "application/x-msdownload"},
|
||||
{".dat", "application/octet-stream"},
|
||||
// General
|
||||
{".323", "text/h323"},
|
||||
{".3g2", "video/3gpp2"},
|
||||
{".3gp", "video/3gpp"},
|
||||
{".3gp2", "video/3gpp2"},
|
||||
{".3gpp", "video/3gpp"},
|
||||
{".7z", "application/x-7z-compressed"},
|
||||
{".aa", "audio/audible"},
|
||||
{".AAC", "audio/aac"},
|
||||
{".aaf", "application/octet-stream"},
|
||||
{".aax", "audio/vnd.audible.aax"},
|
||||
{".ac3", "audio/ac3"},
|
||||
{".aca", "application/octet-stream"},
|
||||
{".accda", "application/msaccess.addin"},
|
||||
{".accdb", "application/msaccess"},
|
||||
{".accdc", "application/msaccess.cab"},
|
||||
{".accde", "application/msaccess"},
|
||||
{".accdr", "application/msaccess.runtime"},
|
||||
{".accdt", "application/msaccess"},
|
||||
{".accdw", "application/msaccess.webapplication"},
|
||||
{".accft", "application/msaccess.ftemplate"},
|
||||
{".acx", "application/internet-property-stream"},
|
||||
{".AddIn", "text/xml"},
|
||||
{".ade", "application/msaccess"},
|
||||
{".adobebridge", "application/x-bridge-url"},
|
||||
{".adp", "application/msaccess"},
|
||||
{".ADT", "audio/vnd.dlna.adts"},
|
||||
{".ADTS", "audio/aac"},
|
||||
{".afm", "application/octet-stream"},
|
||||
{".ai", "application/postscript"},
|
||||
{".aif", "audio/aiff"},
|
||||
{".aifc", "audio/aiff"},
|
||||
{".aiff", "audio/aiff"},
|
||||
{".air", "application/vnd.adobe.air-application-installer-package+zip"},
|
||||
{".amc", "application/mpeg"},
|
||||
{".anx", "application/annodex"},
|
||||
{".apk", "application/vnd.android.package-archive"},
|
||||
{".application", "application/x-ms-application"},
|
||||
{".art", "image/x-jg"},
|
||||
{".asa", "application/xml"},
|
||||
{".asax", "application/xml"},
|
||||
{".ascx", "application/xml"},
|
||||
{".asd", "application/octet-stream"},
|
||||
{".asf", "video/x-ms-asf"},
|
||||
{".ashx", "application/xml"},
|
||||
{".asi", "application/octet-stream"},
|
||||
{".asm", "text/plain"},
|
||||
{".asmx", "application/xml"},
|
||||
{".aspx", "application/xml"},
|
||||
{".asr", "video/x-ms-asf"},
|
||||
{".asx", "video/x-ms-asf"},
|
||||
{".atom", "application/atom+xml"},
|
||||
{".au", "audio/basic"},
|
||||
{".avi", "video/x-msvideo"},
|
||||
{".axa", "audio/annodex"},
|
||||
{".axs", "application/olescript"},
|
||||
{".axv", "video/annodex"},
|
||||
{".bas", "text/plain"},
|
||||
{".bcpio", "application/x-bcpio"},
|
||||
{".bin", "application/octet-stream"},
|
||||
{".bmp", "image/bmp"},
|
||||
{".c", "text/plain"},
|
||||
{".cab", "application/octet-stream"},
|
||||
{".caf", "audio/x-caf"},
|
||||
{".calx", "application/vnd.ms-office.calx"},
|
||||
{".cat", "application/vnd.ms-pki.seccat"},
|
||||
{".cc", "text/plain"},
|
||||
{".cd", "text/plain"},
|
||||
{".cdda", "audio/aiff"},
|
||||
{".cdf", "application/x-cdf"},
|
||||
{".cer", "application/x-x509-ca-cert"},
|
||||
{".cfg", "text/plain"},
|
||||
{".chm", "application/octet-stream"},
|
||||
{".class", "application/x-java-applet"},
|
||||
{".clp", "application/x-msclip"},
|
||||
{".cmd", "text/plain"},
|
||||
{".cmx", "image/x-cmx"},
|
||||
{".cnf", "text/plain"},
|
||||
{".cod", "image/cis-cod"},
|
||||
{".config", "application/xml"},
|
||||
{".contact", "text/x-ms-contact"},
|
||||
{".coverage", "application/xml"},
|
||||
{".cpio", "application/x-cpio"},
|
||||
{".cpp", "text/plain"},
|
||||
{".crd", "application/x-mscardfile"},
|
||||
{".crl", "application/pkix-crl"},
|
||||
{".crt", "application/x-x509-ca-cert"},
|
||||
{".cs", "text/plain"},
|
||||
{".csdproj", "text/plain"},
|
||||
{".csh", "application/x-csh"},
|
||||
{".csproj", "text/plain"},
|
||||
{".css", "text/css"},
|
||||
{".csv", "text/csv"},
|
||||
{".cur", "application/octet-stream"},
|
||||
{".cxx", "text/plain"},
|
||||
{".datasource", "application/xml"},
|
||||
{".dbproj", "text/plain"},
|
||||
{".dcr", "application/x-director"},
|
||||
{".def", "text/plain"},
|
||||
{".deploy", "application/octet-stream"},
|
||||
{".der", "application/x-x509-ca-cert"},
|
||||
{".dgml", "application/xml"},
|
||||
{".dib", "image/bmp"},
|
||||
{".dif", "video/x-dv"},
|
||||
{".dir", "application/x-director"},
|
||||
{".disco", "text/xml"},
|
||||
{".divx", "video/divx"},
|
||||
{".dll", "application/x-msdownload"},
|
||||
{".dll.config", "text/xml"},
|
||||
{".dlm", "text/dlm"},
|
||||
{".doc", "application/msword"},
|
||||
{".docm", "application/vnd.ms-word.document.macroEnabled.12"},
|
||||
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
|
||||
{".dot", "application/msword"},
|
||||
{".dotm", "application/vnd.ms-word.template.macroEnabled.12"},
|
||||
{".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
|
||||
{".dsp", "application/octet-stream"},
|
||||
{".dsw", "text/plain"},
|
||||
{".dtd", "text/xml"},
|
||||
{".dtsConfig", "text/xml"},
|
||||
{".dv", "video/x-dv"},
|
||||
{".dvi", "application/x-dvi"},
|
||||
{".dwf", "drawing/x-dwf"},
|
||||
{".dwg", "application/acad"},
|
||||
{".dwp", "application/octet-stream"},
|
||||
{".dxf", "application/x-dxf"},
|
||||
{".dxr", "application/x-director"},
|
||||
{".eml", "message/rfc822"},
|
||||
{".emz", "application/octet-stream"},
|
||||
{".eot", "application/vnd.ms-fontobject"},
|
||||
{".eps", "application/postscript"},
|
||||
{".es", "application/ecmascript"},
|
||||
{".etl", "application/etl"},
|
||||
{".etx", "text/x-setext"},
|
||||
{".evy", "application/envoy"},
|
||||
|
||||
{".exe.config", "text/xml"},
|
||||
{".f4v", "video/mp4"},
|
||||
{".fdf", "application/vnd.fdf"},
|
||||
{".fif", "application/fractals"},
|
||||
{".filters", "application/xml"},
|
||||
{".fla", "application/octet-stream"},
|
||||
{".flac", "audio/flac"},
|
||||
{".flr", "x-world/x-vrml"},
|
||||
{".flv", "video/x-flv"},
|
||||
{".fsscript", "application/fsharp-script"},
|
||||
{".fsx", "application/fsharp-script"},
|
||||
{".generictest", "application/xml"},
|
||||
{".gif", "image/gif"},
|
||||
{".gpx", "application/gpx+xml"},
|
||||
{".group", "text/x-ms-group"},
|
||||
{".gsm", "audio/x-gsm"},
|
||||
{".gtar", "application/x-gtar"},
|
||||
{".gz", "application/x-gzip"},
|
||||
{".h", "text/plain"},
|
||||
{".hdf", "application/x-hdf"},
|
||||
{".hdml", "text/x-hdml"},
|
||||
{".hhc", "application/x-oleobject"},
|
||||
{".hhk", "application/octet-stream"},
|
||||
{".hhp", "application/octet-stream"},
|
||||
{".hlp", "application/winhlp"},
|
||||
{".hpp", "text/plain"},
|
||||
{".hqx", "application/mac-binhex40"},
|
||||
{".hta", "application/hta"},
|
||||
{".htc", "text/x-component"},
|
||||
{".htm", "text/html"},
|
||||
{".html", "text/html"},
|
||||
{".htt", "text/webviewhtml"},
|
||||
{".hxa", "application/xml"},
|
||||
{".hxc", "application/xml"},
|
||||
{".hxd", "application/octet-stream"},
|
||||
{".hxe", "application/xml"},
|
||||
{".hxf", "application/xml"},
|
||||
{".hxh", "application/octet-stream"},
|
||||
{".hxi", "application/octet-stream"},
|
||||
{".hxk", "application/xml"},
|
||||
{".hxq", "application/octet-stream"},
|
||||
{".hxr", "application/octet-stream"},
|
||||
{".hxs", "application/octet-stream"},
|
||||
{".hxt", "text/html"},
|
||||
{".hxv", "application/xml"},
|
||||
{".hxw", "application/octet-stream"},
|
||||
{".hxx", "text/plain"},
|
||||
{".i", "text/plain"},
|
||||
{".ico", "image/x-icon"},
|
||||
{".ics", "application/octet-stream"},
|
||||
{".idl", "text/plain"},
|
||||
{".ief", "image/ief"},
|
||||
{".iii", "application/x-iphone"},
|
||||
{".inc", "text/plain"},
|
||||
{".inf", "application/octet-stream"},
|
||||
{".ini", "text/plain"},
|
||||
{".inl", "text/plain"},
|
||||
{".ins", "application/x-internet-signup"},
|
||||
{".ipa", "application/x-itunes-ipa"},
|
||||
{".ipg", "application/x-itunes-ipg"},
|
||||
{".ipproj", "text/plain"},
|
||||
{".ipsw", "application/x-itunes-ipsw"},
|
||||
{".iqy", "text/x-ms-iqy"},
|
||||
{".isp", "application/x-internet-signup"},
|
||||
{".isma", "application/octet-stream"},
|
||||
{".ismv", "application/octet-stream"},
|
||||
{".ite", "application/x-itunes-ite"},
|
||||
{".itlp", "application/x-itunes-itlp"},
|
||||
{".itms", "application/x-itunes-itms"},
|
||||
{".itpc", "application/x-itunes-itpc"},
|
||||
{".IVF", "video/x-ivf"},
|
||||
{".jar", "application/java-archive"},
|
||||
{".java", "application/octet-stream"},
|
||||
{".jck", "application/liquidmotion"},
|
||||
{".jcz", "application/liquidmotion"},
|
||||
{".jfif", "image/pjpeg"},
|
||||
{".jnlp", "application/x-java-jnlp-file"},
|
||||
{".jpb", "application/octet-stream"},
|
||||
{".jpe", "image/jpeg"},
|
||||
{".jpeg", "image/jpeg"},
|
||||
{".jpg", "image/jpeg"},
|
||||
{".js", "application/javascript"},
|
||||
{".json", "application/json"},
|
||||
{".jsx", "text/jscript"},
|
||||
{".jsxbin", "text/plain"},
|
||||
{".latex", "application/x-latex"},
|
||||
{".library-ms", "application/windows-library+xml"},
|
||||
{".lit", "application/x-ms-reader"},
|
||||
{".loadtest", "application/xml"},
|
||||
{".lpk", "application/octet-stream"},
|
||||
{".lsf", "video/x-la-asf"},
|
||||
{".lst", "text/plain"},
|
||||
{".lsx", "video/x-la-asf"},
|
||||
{".lzh", "application/octet-stream"},
|
||||
{".m13", "application/x-msmediaview"},
|
||||
{".m14", "application/x-msmediaview"},
|
||||
{".m1v", "video/mpeg"},
|
||||
{".m2t", "video/vnd.dlna.mpeg-tts"},
|
||||
{".m2ts", "video/vnd.dlna.mpeg-tts"},
|
||||
{".m2v", "video/mpeg"},
|
||||
{".m3u", "audio/x-mpegurl"},
|
||||
{".m3u8", "audio/x-mpegurl"},
|
||||
{".m4a", "audio/m4a"},
|
||||
{".m4b", "audio/m4b"},
|
||||
{".m4p", "audio/m4p"},
|
||||
{".m4r", "audio/x-m4r"},
|
||||
{".m4v", "video/x-m4v"},
|
||||
{".mac", "image/x-macpaint"},
|
||||
{".mak", "text/plain"},
|
||||
{".man", "application/x-troff-man"},
|
||||
{".manifest", "application/x-ms-manifest"},
|
||||
{".map", "text/plain"},
|
||||
{".master", "application/xml"},
|
||||
{".mbox", "application/mbox"},
|
||||
{".mda", "application/msaccess"},
|
||||
{".mdb", "application/x-msaccess"},
|
||||
{".mde", "application/msaccess"},
|
||||
{".mdp", "application/octet-stream"},
|
||||
{".me", "application/x-troff-me"},
|
||||
{".mfp", "application/x-shockwave-flash"},
|
||||
{".mht", "message/rfc822"},
|
||||
{".mhtml", "message/rfc822"},
|
||||
{".mid", "audio/mid"},
|
||||
{".midi", "audio/mid"},
|
||||
{".mix", "application/octet-stream"},
|
||||
{".mk", "text/plain"},
|
||||
{".mk3d", "video/x-matroska-3d"},
|
||||
{".mka", "audio/x-matroska"},
|
||||
{".mkv", "video/x-matroska"},
|
||||
{".mmf", "application/x-smaf"},
|
||||
{".mno", "text/xml"},
|
||||
{".mny", "application/x-msmoney"},
|
||||
{".mod", "video/mpeg"},
|
||||
{".mov", "video/quicktime"},
|
||||
{".movie", "video/x-sgi-movie"},
|
||||
{".mp2", "video/mpeg"},
|
||||
{".mp2v", "video/mpeg"},
|
||||
{".mp3", "audio/mpeg"},
|
||||
{".mp4", "video/mp4"},
|
||||
{".mp4v", "video/mp4"},
|
||||
{".mpa", "video/mpeg"},
|
||||
{".mpe", "video/mpeg"},
|
||||
{".mpeg", "video/mpeg"},
|
||||
{".mpf", "application/vnd.ms-mediapackage"},
|
||||
{".mpg", "video/mpeg"},
|
||||
{".mpp", "application/vnd.ms-project"},
|
||||
{".mpv2", "video/mpeg"},
|
||||
{".mqv", "video/quicktime"},
|
||||
{".ms", "application/x-troff-ms"},
|
||||
{".msg", "application/vnd.ms-outlook"},
|
||||
{".msi", "application/octet-stream"},
|
||||
{".mso", "application/octet-stream"},
|
||||
{".mts", "video/vnd.dlna.mpeg-tts"},
|
||||
{".mtx", "application/xml"},
|
||||
{".mvb", "application/x-msmediaview"},
|
||||
{".mvc", "application/x-miva-compiled"},
|
||||
{".mxf", "application/mxf"},
|
||||
{".mxp", "application/x-mmxp"},
|
||||
{".nc", "application/x-netcdf"},
|
||||
{".nsc", "video/x-ms-asf"},
|
||||
{".nws", "message/rfc822"},
|
||||
{".ocx", "application/octet-stream"},
|
||||
{".oda", "application/oda"},
|
||||
{".odb", "application/vnd.oasis.opendocument.database"},
|
||||
{".odc", "application/vnd.oasis.opendocument.chart"},
|
||||
{".odf", "application/vnd.oasis.opendocument.formula"},
|
||||
{".odg", "application/vnd.oasis.opendocument.graphics"},
|
||||
{".odh", "text/plain"},
|
||||
{".odi", "application/vnd.oasis.opendocument.image"},
|
||||
{".odl", "text/plain"},
|
||||
{".odm", "application/vnd.oasis.opendocument.text-master"},
|
||||
{".odp", "application/vnd.oasis.opendocument.presentation"},
|
||||
{".ods", "application/vnd.oasis.opendocument.spreadsheet"},
|
||||
{".odt", "application/vnd.oasis.opendocument.text"},
|
||||
{".oga", "audio/ogg"},
|
||||
{".ogg", "audio/ogg"},
|
||||
{".ogv", "video/ogg"},
|
||||
{".ogx", "application/ogg"},
|
||||
{".one", "application/onenote"},
|
||||
{".onea", "application/onenote"},
|
||||
{".onepkg", "application/onenote"},
|
||||
{".onetmp", "application/onenote"},
|
||||
{".onetoc", "application/onenote"},
|
||||
{".onetoc2", "application/onenote"},
|
||||
{".opus", "audio/ogg"},
|
||||
{".orderedtest", "application/xml"},
|
||||
{".osdx", "application/opensearchdescription+xml"},
|
||||
{".otf", "application/font-sfnt"},
|
||||
{".otg", "application/vnd.oasis.opendocument.graphics-template"},
|
||||
{".oth", "application/vnd.oasis.opendocument.text-web"},
|
||||
{".otp", "application/vnd.oasis.opendocument.presentation-template"},
|
||||
{".ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
|
||||
{".ott", "application/vnd.oasis.opendocument.text-template"},
|
||||
{".oxt", "application/vnd.openofficeorg.extension"},
|
||||
{".p10", "application/pkcs10"},
|
||||
{".p12", "application/x-pkcs12"},
|
||||
{".p7b", "application/x-pkcs7-certificates"},
|
||||
{".p7c", "application/pkcs7-mime"},
|
||||
{".p7m", "application/pkcs7-mime"},
|
||||
{".p7r", "application/x-pkcs7-certreqresp"},
|
||||
{".p7s", "application/pkcs7-signature"},
|
||||
{".pbm", "image/x-portable-bitmap"},
|
||||
{".pcast", "application/x-podcast"},
|
||||
{".pct", "image/pict"},
|
||||
{".pcx", "application/octet-stream"},
|
||||
{".pcz", "application/octet-stream"},
|
||||
{".pdf", "application/pdf"},
|
||||
{".pfb", "application/octet-stream"},
|
||||
{".pfm", "application/octet-stream"},
|
||||
{".pfx", "application/x-pkcs12"},
|
||||
{".pgm", "image/x-portable-graymap"},
|
||||
{".pic", "image/pict"},
|
||||
{".pict", "image/pict"},
|
||||
{".pkgdef", "text/plain"},
|
||||
{".pkgundef", "text/plain"},
|
||||
{".pko", "application/vnd.ms-pki.pko"},
|
||||
{".pls", "audio/scpls"},
|
||||
{".pma", "application/x-perfmon"},
|
||||
{".pmc", "application/x-perfmon"},
|
||||
{".pml", "application/x-perfmon"},
|
||||
{".pmr", "application/x-perfmon"},
|
||||
{".pmw", "application/x-perfmon"},
|
||||
{".png", "image/png"},
|
||||
{".pnm", "image/x-portable-anymap"},
|
||||
{".pnt", "image/x-macpaint"},
|
||||
{".pntg", "image/x-macpaint"},
|
||||
{".pnz", "image/png"},
|
||||
{".pot", "application/vnd.ms-powerpoint"},
|
||||
{".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},
|
||||
{".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
|
||||
{".ppa", "application/vnd.ms-powerpoint"},
|
||||
{".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},
|
||||
{".ppm", "image/x-portable-pixmap"},
|
||||
{".pps", "application/vnd.ms-powerpoint"},
|
||||
{".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
|
||||
{".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
|
||||
{".ppt", "application/vnd.ms-powerpoint"},
|
||||
{".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
|
||||
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
|
||||
{".prf", "application/pics-rules"},
|
||||
{".prm", "application/octet-stream"},
|
||||
{".prx", "application/octet-stream"},
|
||||
{".ps", "application/postscript"},
|
||||
{".psc1", "application/PowerShell"},
|
||||
{".psd", "application/octet-stream"},
|
||||
{".psess", "application/xml"},
|
||||
{".psm", "application/octet-stream"},
|
||||
{".psp", "application/octet-stream"},
|
||||
{".pst", "application/vnd.ms-outlook"},
|
||||
{".pub", "application/x-mspublisher"},
|
||||
{".pwz", "application/vnd.ms-powerpoint"},
|
||||
{".qht", "text/x-html-insertion"},
|
||||
{".qhtm", "text/x-html-insertion"},
|
||||
{".qt", "video/quicktime"},
|
||||
{".qti", "image/x-quicktime"},
|
||||
{".qtif", "image/x-quicktime"},
|
||||
{".qtl", "application/x-quicktimeplayer"},
|
||||
{".qxd", "application/octet-stream"},
|
||||
{".ra", "audio/x-pn-realaudio"},
|
||||
{".ram", "audio/x-pn-realaudio"},
|
||||
{".rar", "application/x-rar-compressed"},
|
||||
{".ras", "image/x-cmu-raster"},
|
||||
{".rat", "application/rat-file"},
|
||||
{".rc", "text/plain"},
|
||||
{".rc2", "text/plain"},
|
||||
{".rct", "text/plain"},
|
||||
{".rdlc", "application/xml"},
|
||||
{".reg", "text/plain"},
|
||||
{".resx", "application/xml"},
|
||||
{".rf", "image/vnd.rn-realflash"},
|
||||
{".rgb", "image/x-rgb"},
|
||||
{".rgs", "text/plain"},
|
||||
{".rm", "application/vnd.rn-realmedia"},
|
||||
{".rmi", "audio/mid"},
|
||||
{".rmp", "application/vnd.rn-rn_music_package"},
|
||||
{".roff", "application/x-troff"},
|
||||
{".rpm", "audio/x-pn-realaudio-plugin"},
|
||||
{".rqy", "text/x-ms-rqy"},
|
||||
{".rtf", "application/rtf"},
|
||||
{".rtx", "text/richtext"},
|
||||
{".rvt", "application/octet-stream"},
|
||||
{".ruleset", "application/xml"},
|
||||
{".s", "text/plain"},
|
||||
{".safariextz", "application/x-safari-safariextz"},
|
||||
{".scd", "application/x-msschedule"},
|
||||
{".scr", "text/plain"},
|
||||
{".sct", "text/scriptlet"},
|
||||
{".sd2", "audio/x-sd2"},
|
||||
{".sdp", "application/sdp"},
|
||||
{".sea", "application/octet-stream"},
|
||||
{".searchConnector-ms", "application/windows-search-connector+xml"},
|
||||
{".setpay", "application/set-payment-initiation"},
|
||||
{".setreg", "application/set-registration-initiation"},
|
||||
{".settings", "application/xml"},
|
||||
{".sgimb", "application/x-sgimb"},
|
||||
{".sgml", "text/sgml"},
|
||||
{".sh", "application/x-sh"},
|
||||
{".shar", "application/x-shar"},
|
||||
{".shtml", "text/html"},
|
||||
{".sit", "application/x-stuffit"},
|
||||
{".sitemap", "application/xml"},
|
||||
{".skin", "application/xml"},
|
||||
{".skp", "application/x-koan"},
|
||||
{".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"},
|
||||
{".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
|
||||
{".slk", "application/vnd.ms-excel"},
|
||||
{".sln", "text/plain"},
|
||||
{".slupkg-ms", "application/x-ms-license"},
|
||||
{".smd", "audio/x-smd"},
|
||||
{".smi", "application/octet-stream"},
|
||||
{".smx", "audio/x-smd"},
|
||||
{".smz", "audio/x-smd"},
|
||||
{".snd", "audio/basic"},
|
||||
{".snippet", "application/xml"},
|
||||
{".snp", "application/octet-stream"},
|
||||
{".sol", "text/plain"},
|
||||
{".sor", "text/plain"},
|
||||
{".spc", "application/x-pkcs7-certificates"},
|
||||
{".spl", "application/futuresplash"},
|
||||
{".spx", "audio/ogg"},
|
||||
{".src", "application/x-wais-source"},
|
||||
{".srf", "text/plain"},
|
||||
{".SSISDeploymentManifest", "text/xml"},
|
||||
{".ssm", "application/streamingmedia"},
|
||||
{".sst", "application/vnd.ms-pki.certstore"},
|
||||
{".stl", "application/vnd.ms-pki.stl"},
|
||||
{".sv4cpio", "application/x-sv4cpio"},
|
||||
{".sv4crc", "application/x-sv4crc"},
|
||||
{".svc", "application/xml"},
|
||||
{".svg", "image/svg+xml"},
|
||||
{".swf", "application/x-shockwave-flash"},
|
||||
{".step", "application/step"},
|
||||
{".stp", "application/step"},
|
||||
{".t", "application/x-troff"},
|
||||
{".tar", "application/x-tar"},
|
||||
{".tcl", "application/x-tcl"},
|
||||
{".testrunconfig", "application/xml"},
|
||||
{".testsettings", "application/xml"},
|
||||
{".tex", "application/x-tex"},
|
||||
{".texi", "application/x-texinfo"},
|
||||
{".texinfo", "application/x-texinfo"},
|
||||
{".tgz", "application/x-compressed"},
|
||||
{".thmx", "application/vnd.ms-officetheme"},
|
||||
{".thn", "application/octet-stream"},
|
||||
{".tif", "image/tiff"},
|
||||
{".tiff", "image/tiff"},
|
||||
{".tlh", "text/plain"},
|
||||
{".tli", "text/plain"},
|
||||
{".toc", "application/octet-stream"},
|
||||
{".tr", "application/x-troff"},
|
||||
{".trm", "application/x-msterminal"},
|
||||
{".trx", "application/xml"},
|
||||
{".ts", "video/vnd.dlna.mpeg-tts"},
|
||||
{".tsv", "text/tab-separated-values"},
|
||||
{".ttf", "application/font-sfnt"},
|
||||
{".tts", "video/vnd.dlna.mpeg-tts"},
|
||||
{".txt", "text/plain"},
|
||||
{".u32", "application/octet-stream"},
|
||||
{".uls", "text/iuls"},
|
||||
{".user", "text/plain"},
|
||||
{".ustar", "application/x-ustar"},
|
||||
{".vb", "text/plain"},
|
||||
{".vbdproj", "text/plain"},
|
||||
{".vbk", "video/mpeg"},
|
||||
{".vbproj", "text/plain"},
|
||||
{".vbs", "text/vbscript"},
|
||||
{".vcf", "text/x-vcard"},
|
||||
{".vcproj", "application/xml"},
|
||||
{".vcs", "text/plain"},
|
||||
{".vcxproj", "application/xml"},
|
||||
{".vddproj", "text/plain"},
|
||||
{".vdp", "text/plain"},
|
||||
{".vdproj", "text/plain"},
|
||||
{".vdx", "application/vnd.ms-visio.viewer"},
|
||||
{".vml", "text/xml"},
|
||||
{".vscontent", "application/xml"},
|
||||
{".vsct", "text/xml"},
|
||||
{".vsd", "application/vnd.visio"},
|
||||
{".vsi", "application/ms-vsi"},
|
||||
{".vsix", "application/vsix"},
|
||||
{".vsixlangpack", "text/xml"},
|
||||
{".vsixmanifest", "text/xml"},
|
||||
{".vsmdi", "application/xml"},
|
||||
{".vspscc", "text/plain"},
|
||||
{".vss", "application/vnd.visio"},
|
||||
{".vsscc", "text/plain"},
|
||||
{".vssettings", "text/xml"},
|
||||
{".vssscc", "text/plain"},
|
||||
{".vst", "application/vnd.visio"},
|
||||
{".vstemplate", "text/xml"},
|
||||
{".vsto", "application/x-ms-vsto"},
|
||||
{".vsw", "application/vnd.visio"},
|
||||
{".vsx", "application/vnd.visio"},
|
||||
{".vtt", "text/vtt"},
|
||||
{".vtx", "application/vnd.visio"},
|
||||
{".wasm", "application/wasm"},
|
||||
{".wav", "audio/wav"},
|
||||
{".wave", "audio/wav"},
|
||||
{".wax", "audio/x-ms-wax"},
|
||||
{".wbk", "application/msword"},
|
||||
{".wbmp", "image/vnd.wap.wbmp"},
|
||||
{".wcm", "application/vnd.ms-works"},
|
||||
{".wdb", "application/vnd.ms-works"},
|
||||
{".wdp", "image/vnd.ms-photo"},
|
||||
{".webarchive", "application/x-safari-webarchive"},
|
||||
{".webm", "video/webm"},
|
||||
{".webp", "image/webp"},
|
||||
{".webtest", "application/xml"},
|
||||
{".wiq", "application/xml"},
|
||||
{".wiz", "application/msword"},
|
||||
{".wks", "application/vnd.ms-works"},
|
||||
{".WLMP", "application/wlmoviemaker"},
|
||||
{".wlpginstall", "application/x-wlpg-detect"},
|
||||
{".wlpginstall3", "application/x-wlpg3-detect"},
|
||||
{".wm", "video/x-ms-wm"},
|
||||
{".wma", "audio/x-ms-wma"},
|
||||
{".wmd", "application/x-ms-wmd"},
|
||||
{".wmf", "application/x-msmetafile"},
|
||||
{".wml", "text/vnd.wap.wml"},
|
||||
{".wmlc", "application/vnd.wap.wmlc"},
|
||||
{".wmls", "text/vnd.wap.wmlscript"},
|
||||
{".wmlsc", "application/vnd.wap.wmlscriptc"},
|
||||
{".wmp", "video/x-ms-wmp"},
|
||||
{".wmv", "video/x-ms-wmv"},
|
||||
{".wmx", "video/x-ms-wmx"},
|
||||
{".wmz", "application/x-ms-wmz"},
|
||||
{".woff", "application/font-woff"},
|
||||
{".woff2", "application/font-woff2"},
|
||||
{".wpl", "application/vnd.ms-wpl"},
|
||||
{".wps", "application/vnd.ms-works"},
|
||||
{".wri", "application/x-mswrite"},
|
||||
{".wrl", "x-world/x-vrml"},
|
||||
{".wrz", "x-world/x-vrml"},
|
||||
{".wsc", "text/scriptlet"},
|
||||
{".wsdl", "text/xml"},
|
||||
{".wvx", "video/x-ms-wvx"},
|
||||
{".x", "application/directx"},
|
||||
{".xaf", "x-world/x-vrml"},
|
||||
{".xaml", "application/xaml+xml"},
|
||||
{".xap", "application/x-silverlight-app"},
|
||||
{".xbap", "application/x-ms-xbap"},
|
||||
{".xbm", "image/x-xbitmap"},
|
||||
{".xdr", "text/plain"},
|
||||
{".xht", "application/xhtml+xml"},
|
||||
{".xhtml", "application/xhtml+xml"},
|
||||
{".xla", "application/vnd.ms-excel"},
|
||||
{".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"},
|
||||
{".xlc", "application/vnd.ms-excel"},
|
||||
{".xld", "application/vnd.ms-excel"},
|
||||
{".xlk", "application/vnd.ms-excel"},
|
||||
{".xll", "application/vnd.ms-excel"},
|
||||
{".xlm", "application/vnd.ms-excel"},
|
||||
{".xls", "application/vnd.ms-excel"},
|
||||
{".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
|
||||
{".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},
|
||||
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
|
||||
{".xlt", "application/vnd.ms-excel"},
|
||||
{".xltm", "application/vnd.ms-excel.template.macroEnabled.12"},
|
||||
{".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
|
||||
{".xlw", "application/vnd.ms-excel"},
|
||||
{".xmp", "application/octet-stream"},
|
||||
{".xmta", "application/xml"},
|
||||
{".xof", "x-world/x-vrml"},
|
||||
{".XOML", "text/plain"},
|
||||
{".xpm", "image/x-xpixmap"},
|
||||
{".xps", "application/vnd.ms-xpsdocument"},
|
||||
{".xrm-ms", "text/xml"},
|
||||
{".xsc", "application/xml"},
|
||||
{".xsd", "text/xml"},
|
||||
{".xsf", "text/xml"},
|
||||
{".xsl", "text/xml"},
|
||||
{".xslt", "text/xml"},
|
||||
{".xsn", "application/octet-stream"},
|
||||
{".xss", "application/xml"},
|
||||
{".xspf", "application/xspf+xml"},
|
||||
{".xtp", "application/octet-stream"},
|
||||
{".xwd", "image/x-xwindowdump"},
|
||||
{".z", "application/x-compress"},
|
||||
{".zip", "application/zip"}
|
||||
};
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public static string GetMimeType(string extension)
|
||||
{
|
||||
if (extension == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!extension.StartsWith("."))
|
||||
{
|
||||
extension = "." + extension;
|
||||
}
|
||||
|
||||
string mime;
|
||||
|
||||
return _mappings.TryGetValue(extension, out mime) ? mime : "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
14
Mhf.Server/Web/Route/FileWebRoute.cs
Normal file
14
Mhf.Server/Web/Route/FileWebRoute.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace Mhf.Server.Web.Route
|
||||
{
|
||||
public abstract class FileWebRoute : WebRoute
|
||||
{
|
||||
protected IFileProvider FileProvider { get; }
|
||||
|
||||
public FileWebRoute(IFileProvider fileProvider)
|
||||
{
|
||||
FileProvider = fileProvider;
|
||||
}
|
||||
}
|
||||
}
|
16
Mhf.Server/Web/Route/IWebRoute.cs
Normal file
16
Mhf.Server/Web/Route/IWebRoute.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Web.Route
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a handler for a route
|
||||
/// </summary>
|
||||
public interface IWebRoute
|
||||
{
|
||||
string Route { get; }
|
||||
Task<WebResponse> Get(WebRequest request);
|
||||
Task<WebResponse> Post(WebRequest request);
|
||||
Task<WebResponse> Put(WebRequest request);
|
||||
Task<WebResponse> Delete(WebRequest request);
|
||||
}
|
||||
}
|
10
Mhf.Server/Web/Route/IWebRouter.cs
Normal file
10
Mhf.Server/Web/Route/IWebRouter.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Web.Route
|
||||
{
|
||||
public interface IWebRouter
|
||||
{
|
||||
void AddRoute(IWebRoute route);
|
||||
Task<WebResponse> Route(WebRequest request);
|
||||
}
|
||||
}
|
12
Mhf.Server/Web/Route/ServerWebRoute.cs
Normal file
12
Mhf.Server/Web/Route/ServerWebRoute.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Mhf.Server.Web.Route
|
||||
{
|
||||
public abstract class ServerWebRoute : WebRoute
|
||||
{
|
||||
protected MhfServer Server { get; }
|
||||
|
||||
public ServerWebRoute(MhfServer server)
|
||||
{
|
||||
Server = server;
|
||||
}
|
||||
}
|
||||
}
|
35
Mhf.Server/Web/Route/WebRoute.cs
Normal file
35
Mhf.Server/Web/Route/WebRoute.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Threading.Tasks;
|
||||
using Arrowgene.Services.Logging;
|
||||
|
||||
namespace Mhf.Server.Web.Route
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of Kestrel server as backend
|
||||
/// </summary>
|
||||
public abstract class WebRoute : IWebRoute
|
||||
{
|
||||
protected ILogger Logger => LogProvider.Instance.GetLogger(this);
|
||||
|
||||
public abstract string Route { get; }
|
||||
|
||||
public virtual Task<WebResponse> Get(WebRequest request)
|
||||
{
|
||||
return WebResponse.NotFound();
|
||||
}
|
||||
|
||||
public virtual Task<WebResponse> Post(WebRequest request)
|
||||
{
|
||||
return WebResponse.NotFound();
|
||||
}
|
||||
|
||||
public virtual Task<WebResponse> Put(WebRequest request)
|
||||
{
|
||||
return WebResponse.NotFound();
|
||||
}
|
||||
|
||||
public virtual Task<WebResponse> Delete(WebRequest request)
|
||||
{
|
||||
return WebResponse.NotFound();
|
||||
}
|
||||
}
|
||||
}
|
84
Mhf.Server/Web/Route/WebRouter.cs
Normal file
84
Mhf.Server/Web/Route/WebRouter.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Setting;
|
||||
|
||||
namespace Mhf.Server.Web.Route
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses routes and calls the route
|
||||
/// </summary>
|
||||
public class WebRouter : IWebRouter
|
||||
{
|
||||
private Dictionary<string, IWebRoute> _routes;
|
||||
private ILogger _logger;
|
||||
private MhfSetting _setting;
|
||||
|
||||
public WebRouter(MhfSetting setting)
|
||||
{
|
||||
_setting = setting;
|
||||
_logger = LogProvider.Instance.GetLogger(this);
|
||||
_routes = new Dictionary<string, IWebRoute>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a handler for a specific route.
|
||||
/// </summary>
|
||||
public void AddRoute(IWebRoute route)
|
||||
{
|
||||
_routes.Add(route.Route, route);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Passes incoming requests to the correct route
|
||||
/// </summary>
|
||||
public async Task<WebResponse> Route(WebRequest request)
|
||||
{
|
||||
_logger.Info($"Request: {request}");
|
||||
if (request.Path == null)
|
||||
{
|
||||
_logger.Error($"Request path not set, please check sever request mapping implementation");
|
||||
return await WebResponse.InternalServerError();
|
||||
}
|
||||
|
||||
if (_routes.ContainsKey(request.Path))
|
||||
{
|
||||
IWebRoute route = _routes[request.Path];
|
||||
Task<WebResponse> responseTask = null;
|
||||
switch (request.Method)
|
||||
{
|
||||
case WebRequestMethod.Get:
|
||||
responseTask = route.Get(request);
|
||||
break;
|
||||
case WebRequestMethod.Post:
|
||||
responseTask = route.Post(request);
|
||||
break;
|
||||
case WebRequestMethod.Put:
|
||||
responseTask = route.Put(request);
|
||||
break;
|
||||
case WebRequestMethod.Delete:
|
||||
responseTask = route.Delete(request);
|
||||
break;
|
||||
}
|
||||
|
||||
if (responseTask == null)
|
||||
{
|
||||
_logger.Info($"Request method: {request.Method} not supported for requested path: {request.Path}");
|
||||
return await WebResponse.InternalServerError();
|
||||
}
|
||||
|
||||
WebResponse response = await responseTask;
|
||||
response.RouteFound = true;
|
||||
if (!string.IsNullOrEmpty(_setting.WebSetting.ServerHeader))
|
||||
{
|
||||
response.Header.Add("Server", _setting.WebSetting.ServerHeader);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
return await WebResponse.NotFound();
|
||||
}
|
||||
}
|
||||
}
|
15
Mhf.Server/Web/Server/IWebServer.cs
Normal file
15
Mhf.Server/Web/Server/IWebServer.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Threading.Tasks;
|
||||
using Mhf.Server.Web.Route;
|
||||
|
||||
namespace Mhf.Server.Web.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines web server
|
||||
/// </summary>
|
||||
public interface IWebServer
|
||||
{
|
||||
void SetHandler(IWebServerHandler handler);
|
||||
Task Start();
|
||||
Task Stop();
|
||||
}
|
||||
}
|
9
Mhf.Server/Web/Server/IWebServerHandler.cs
Normal file
9
Mhf.Server/Web/Server/IWebServerHandler.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mhf.Server.Web.Server
|
||||
{
|
||||
public interface IWebServerHandler
|
||||
{
|
||||
Task<WebResponse> Handle(WebRequest request);
|
||||
}
|
||||
}
|
61
Mhf.Server/Web/Server/Kestrel/KestrelLogger.cs
Normal file
61
Mhf.Server/Web/Server/Kestrel/KestrelLogger.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace Mhf.Server.Web.Server.Kestrel
|
||||
{
|
||||
public class KestrelLogger : ILogger
|
||||
{
|
||||
private readonly string _name;
|
||||
private Arrowgene.Services.Logging.ILogger _logger;
|
||||
|
||||
public KestrelLogger(string name)
|
||||
{
|
||||
_name = name;
|
||||
_logger = LogProvider.Logger<Logger>(name);
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel,
|
||||
EventId eventId,
|
||||
TState state,
|
||||
Exception exception,
|
||||
Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
_logger.Exception(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
string message = $"{formatter(state, exception)}";
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Trace:
|
||||
case LogLevel.Debug:
|
||||
case LogLevel.None:
|
||||
case LogLevel.Information:
|
||||
_logger.Debug(message);
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
_logger.Info(message);
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
case LogLevel.Critical:
|
||||
_logger.Error(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
Mhf.Server/Web/Server/Kestrel/KestrelLoggerProvider.cs
Normal file
25
Mhf.Server/Web/Server/Kestrel/KestrelLoggerProvider.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Mhf.Server.Web.Server.Kestrel
|
||||
{
|
||||
public class KestrelLoggerProvider: ILoggerProvider
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, KestrelLogger> _loggers = new ConcurrentDictionary<string, KestrelLogger>();
|
||||
|
||||
public KestrelLoggerProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return _loggers.GetOrAdd(categoryName, name => new KestrelLogger(name));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_loggers.Clear();
|
||||
}
|
||||
}
|
||||
}
|
198
Mhf.Server/Web/Server/Kestrel/KestrelWebServer.cs
Normal file
198
Mhf.Server/Web/Server/Kestrel/KestrelWebServer.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Arrowgene.Services.Logging;
|
||||
using Mhf.Server.Setting;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Internal;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ILogger = Arrowgene.Services.Logging.ILogger;
|
||||
|
||||
namespace Mhf.Server.Web.Server.Kestrel
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of Kestrel server as backend
|
||||
/// </summary>
|
||||
public class KestrelWebServer : IWebServer
|
||||
{
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private ApplicationLifetime _applicationLifetime;
|
||||
private IServer _server;
|
||||
private int _shutdownTimeout = 10000;
|
||||
private IWebServerHandler _handler;
|
||||
private MhfSetting _setting;
|
||||
private ILogger _logger;
|
||||
|
||||
public KestrelWebServer(MhfSetting setting)
|
||||
{
|
||||
_logger = LogProvider.Logger(this);
|
||||
_setting = setting;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public void SetHandler(IWebServerHandler handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
IHttpApplication<HostingApplication.Context> app;
|
||||
try
|
||||
{
|
||||
if (_handler == null)
|
||||
{
|
||||
throw new Exception("Missing Handler - Call SetHandler()");
|
||||
}
|
||||
|
||||
ILoggerFactory loggerFactory = new LoggerFactory();
|
||||
loggerFactory.AddProvider(new KestrelLoggerProvider());
|
||||
ServiceCollection services = new ServiceCollection();
|
||||
services.AddSingleton(loggerFactory);
|
||||
services.AddLogging();
|
||||
|
||||
IServiceProvider serviceProvider = GetProviderFromFactory(services);
|
||||
IOptions<KestrelServerOptions> kestrelServerOptions = Options.Create(new KestrelServerOptions());
|
||||
kestrelServerOptions.Value.ApplicationServices = serviceProvider;
|
||||
kestrelServerOptions.Value.ListenAnyIP(_setting.WebSetting.HttpPort);
|
||||
if (_setting.WebSetting.HttpsEnabled)
|
||||
{
|
||||
kestrelServerOptions.Value.ListenAnyIP(_setting.WebSetting.HttpsPort,
|
||||
listenOptions =>
|
||||
{
|
||||
X509Certificate2 cert = new X509Certificate2(_setting.WebSetting.HttpsCertPath);
|
||||
listenOptions.UseHttps(cert);
|
||||
});
|
||||
}
|
||||
|
||||
kestrelServerOptions.Value.AddServerHeader = false;
|
||||
|
||||
IOptions<SocketTransportOptions> socketTransportOptions = Options.Create(new SocketTransportOptions());
|
||||
_applicationLifetime = new ApplicationLifetime(
|
||||
loggerFactory.CreateLogger<ApplicationLifetime>()
|
||||
);
|
||||
ITransportFactory transportFactory = new SocketTransportFactory(
|
||||
socketTransportOptions, _applicationLifetime, loggerFactory
|
||||
);
|
||||
|
||||
|
||||
_server = new KestrelServer(kestrelServerOptions, transportFactory, loggerFactory);
|
||||
DiagnosticListener diagnosticListener = new DiagnosticListener("a");
|
||||
IOptions<FormOptions> formOptions = Options.Create(new FormOptions());
|
||||
IHttpContextFactory httpContextFactory = new HttpContextFactory(formOptions);
|
||||
app = new HostingApplication(
|
||||
RequestDelegate,
|
||||
loggerFactory.CreateLogger<KestrelWebServer>(),
|
||||
diagnosticListener,
|
||||
httpContextFactory
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
Task kestrelStartup = _server.StartAsync(app, _cancellationTokenSource.Token);
|
||||
await kestrelStartup;
|
||||
_cancellationTokenSource.Token.Register(
|
||||
state => ((IApplicationLifetime) state).StopApplication(),
|
||||
_applicationLifetime
|
||||
);
|
||||
TaskCompletionSource<object> completionSource = new TaskCompletionSource<object>(
|
||||
TaskCreationOptions.RunContinuationsAsynchronously
|
||||
);
|
||||
_applicationLifetime.ApplicationStopping.Register(
|
||||
obj => ((TaskCompletionSource<object>) obj).TrySetResult(null),
|
||||
completionSource
|
||||
);
|
||||
Task<object> kestrelCompleted = completionSource.Task;
|
||||
object kestrelCompletedResult = await kestrelCompleted;
|
||||
Task kestrelShutdown = _server.StopAsync(new CancellationToken());
|
||||
await kestrelShutdown;
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
{
|
||||
CancellationToken token = new CancellationTokenSource(_shutdownTimeout).Token;
|
||||
_applicationLifetime?.StopApplication();
|
||||
if (_server != null)
|
||||
{
|
||||
await _server.StopAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_applicationLifetime?.NotifyStopped();
|
||||
HostingEventSource.Log.HostStop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever a web request arrives.
|
||||
/// - Maps Kestrel HttpRequest/HttpResponse to WebRequest/WebResponse
|
||||
/// - Calls router to handle the request
|
||||
/// </summary>
|
||||
private async Task RequestDelegate(HttpContext context)
|
||||
{
|
||||
WebRequest request = new WebRequest();
|
||||
request.Host = context.Request.Host.Host;
|
||||
request.Port = context.Request.Host.Port;
|
||||
request.Method = WebRequest.ParseMethod(context.Request.Method);
|
||||
request.Path = context.Request.Path;
|
||||
request.Scheme = context.Request.Scheme;
|
||||
request.ContentType = context.Request.ContentType;
|
||||
request.QueryString = context.Request.QueryString.Value;
|
||||
request.ContentLength = context.Request.ContentLength;
|
||||
foreach (string key in context.Request.Headers.Keys)
|
||||
{
|
||||
request.Header.Add(key, context.Request.Headers[key]);
|
||||
}
|
||||
|
||||
foreach (string key in context.Request.Query.Keys)
|
||||
{
|
||||
request.QueryParameter.Add(key, context.Request.Query[key]);
|
||||
}
|
||||
|
||||
foreach (string key in context.Request.Cookies.Keys)
|
||||
{
|
||||
request.Cookies.Add(key, context.Request.Cookies[key]);
|
||||
}
|
||||
|
||||
await context.Request.Body.CopyToAsync(request.Body);
|
||||
request.Body.Position = 0;
|
||||
WebResponse response = await _handler.Handle(request);
|
||||
context.Response.StatusCode = response.StatusCode;
|
||||
foreach (string key in response.Header.Keys)
|
||||
{
|
||||
context.Response.Headers.Add(key, response.Header[key]);
|
||||
}
|
||||
|
||||
response.Body.Position = 0;
|
||||
await response.Body.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
|
||||
private IServiceProvider GetProviderFromFactory(IServiceCollection collection)
|
||||
{
|
||||
ServiceProvider provider = collection.BuildServiceProvider();
|
||||
IServiceProviderFactory<IServiceCollection> service =
|
||||
provider.GetService<IServiceProviderFactory<IServiceCollection>>();
|
||||
if (service == null || service is DefaultServiceProviderFactory)
|
||||
{
|
||||
return provider;
|
||||
}
|
||||
|
||||
using (provider)
|
||||
{
|
||||
return service.CreateServiceProvider(service.CreateBuilder(collection));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
Mhf.Server/Web/WebCollection.cs
Normal file
54
Mhf.Server/Web/WebCollection.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mhf.Server.Web
|
||||
{
|
||||
public class WebCollection<TKey, TValue>
|
||||
{
|
||||
private Dictionary<TKey, TValue> _collection;
|
||||
private Func<TKey, TKey> _keyTransformer;
|
||||
|
||||
public WebCollection() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public WebCollection(Func<TKey, TKey> keyTransformer)
|
||||
{
|
||||
_collection = new Dictionary<TKey, TValue>();
|
||||
_keyTransformer = keyTransformer;
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => _collection.Keys;
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (_keyTransformer != null)
|
||||
{
|
||||
key = _keyTransformer(key);
|
||||
}
|
||||
|
||||
_collection.Add(key, value);
|
||||
}
|
||||
|
||||
public TValue Get(TKey key)
|
||||
{
|
||||
if (_collection.TryGetValue(key, out TValue value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return default(TValue);
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return _collection.ContainsKey(key);
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get => _collection[key];
|
||||
set => _collection[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
102
Mhf.Server/Web/WebRequest.cs
Normal file
102
Mhf.Server/Web/WebRequest.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using Mhf.Server.Common;
|
||||
|
||||
namespace Mhf.Server.Web
|
||||
{
|
||||
public class WebRequest
|
||||
{
|
||||
public static WebRequestMethod ParseMethod(string method)
|
||||
{
|
||||
method = method.ToLowerInvariant();
|
||||
switch (method)
|
||||
{
|
||||
case "get": return WebRequestMethod.Get;
|
||||
case "put": return WebRequestMethod.Put;
|
||||
case "post": return WebRequestMethod.Post;
|
||||
case "delete": return WebRequestMethod.Delete;
|
||||
}
|
||||
|
||||
return WebRequestMethod.Unknown;
|
||||
}
|
||||
|
||||
public WebRequestMethod Method { get; set; }
|
||||
public string Host { get; set; }
|
||||
public int? Port { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string Scheme { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
public string QueryString { get; set; }
|
||||
public long? ContentLength { get; set; }
|
||||
[XmlIgnore] public Stream Body { get; set; }
|
||||
public WebCollection<string, string> Header { get; }
|
||||
public WebCollection<string, string> QueryParameter { get; }
|
||||
public WebCollection<string, string> Cookies { get; }
|
||||
|
||||
public WebRequest()
|
||||
{
|
||||
Path = null;
|
||||
Body = new MemoryStream();
|
||||
Header = new WebCollection<string, string>(key => key.ToLowerInvariant());
|
||||
Cookies = new WebCollection<string, string>(key => key.ToLowerInvariant());
|
||||
QueryParameter = new WebCollection<string, string>(key => key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the body.
|
||||
/// </summary>
|
||||
public void ClearBody()
|
||||
{
|
||||
Body.Position = 0;
|
||||
Body.SetLength(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes give bytes from current position till length of bytes.
|
||||
/// </summary>
|
||||
public Task WriteAsync(byte[] bytes)
|
||||
{
|
||||
return WriteAsync(bytes, Body.Position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes give bytes from given position till length of bytes.
|
||||
/// </summary>
|
||||
public Task WriteAsync(byte[] bytes, long position)
|
||||
{
|
||||
Body.Position = position;
|
||||
return Body.WriteAsync(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public Task<byte[]> ReadBytesAsync()
|
||||
{
|
||||
Body.Position = 0;
|
||||
return Util.ReadAsync(Body);
|
||||
}
|
||||
|
||||
public Task<string> ReadStringAsync()
|
||||
{
|
||||
return ReadStringAsync(Encoding.UTF8);
|
||||
}
|
||||
|
||||
public async Task<string> ReadStringAsync(Encoding encoding)
|
||||
{
|
||||
if (encoding == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(encoding));
|
||||
}
|
||||
|
||||
Body.Position = 0;
|
||||
byte[] body = await Util.ReadAsync(Body);
|
||||
return encoding.GetString(body);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Method} {Scheme}://{Host}:{Port?.ToString()}{Path}{QueryString}";
|
||||
}
|
||||
}
|
||||
}
|
11
Mhf.Server/Web/WebRequestMethod.cs
Normal file
11
Mhf.Server/Web/WebRequestMethod.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Mhf.Server.Web
|
||||
{
|
||||
public enum WebRequestMethod
|
||||
{
|
||||
Unknown = -1,
|
||||
Get = 0,
|
||||
Post = 1,
|
||||
Put = 2,
|
||||
Delete = 3
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user