mirror of
https://github.com/MikuLeaks/KianaBH3.git
synced 2025-12-12 21:04:41 +01:00
Init enter game
This commit is contained in:
125
.editorconfig
Normal file
125
.editorconfig
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# IDE0022: 使用方法的程序块主体
|
||||||
|
csharp_style_expression_bodied_methods = false:silent
|
||||||
|
|
||||||
|
[*.cs]
|
||||||
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
# 命名规则
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||||
|
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
# 符号规范
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
# 命名样式
|
||||||
|
|
||||||
|
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||||
|
dotnet_naming_style.begins_with_i.required_suffix =
|
||||||
|
dotnet_naming_style.begins_with_i.word_separator =
|
||||||
|
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
csharp_using_directive_placement = outside_namespace:silent
|
||||||
|
csharp_style_expression_bodied_constructors = false:silent
|
||||||
|
csharp_style_expression_bodied_operators = false:silent
|
||||||
|
csharp_style_expression_bodied_properties = true:silent
|
||||||
|
csharp_style_expression_bodied_indexers = true:silent
|
||||||
|
csharp_style_expression_bodied_accessors = true:silent
|
||||||
|
csharp_style_expression_bodied_lambdas = true:silent
|
||||||
|
csharp_style_expression_bodied_local_functions = false:silent
|
||||||
|
csharp_style_conditional_delegate_call = true:suggestion
|
||||||
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
|
csharp_style_var_when_type_is_apparent = false:silent
|
||||||
|
csharp_style_var_elsewhere = false:silent
|
||||||
|
csharp_prefer_simple_using_statement = true:suggestion
|
||||||
|
csharp_space_around_binary_operators = before_and_after
|
||||||
|
csharp_indent_labels = one_less_than_current
|
||||||
|
|
||||||
|
[*.vb]
|
||||||
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
# 命名规则
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
|
||||||
|
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
# 符号规范
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.类型.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.非字段成员.required_modifiers =
|
||||||
|
|
||||||
|
# 命名样式
|
||||||
|
|
||||||
|
dotnet_naming_style.以_i_开始.required_prefix = I
|
||||||
|
dotnet_naming_style.以_i_开始.required_suffix =
|
||||||
|
dotnet_naming_style.以_i_开始.word_separator =
|
||||||
|
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
|
|
||||||
|
[*.{cs,vb}]
|
||||||
|
end_of_line = crlf
|
||||||
|
dotnet_style_qualification_for_field = false:silent
|
||||||
|
dotnet_style_qualification_for_property = false:silent
|
||||||
|
dotnet_style_qualification_for_method = false:silent
|
||||||
|
dotnet_style_qualification_for_event = false:silent
|
||||||
|
tab_width = 4
|
||||||
|
indent_size = 4
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
370
.gitignore
vendored
Normal file
370
.gitignore
vendored
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
[Ll]aunchSettings.json
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
# *.pubxml
|
||||||
|
# *.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Special Files
|
||||||
|
/SdkServer/Properties
|
||||||
|
/GameServer/OriginalProto
|
||||||
|
*.rar
|
||||||
36
Common/Common.csproj
Normal file
36
Common/Common.csproj
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<CETCompat>false</CETCompat>
|
||||||
|
<RootNamespace>KianaBH</RootNamespace>
|
||||||
|
<AssemblyName>KianaCommon</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EastAsianWidth" Version="1.2.0" />
|
||||||
|
<PackageReference Include="Google.Protobuf" Version="3.29.2" />
|
||||||
|
<PackageReference Include="Google.Protobuf.Tools" Version="3.29.2" />
|
||||||
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Spectre.Console" Version="0.49.1" />
|
||||||
|
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.10" />
|
||||||
|
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.10" />
|
||||||
|
<PackageReference Include="SqlSugarCore" Version="5.1.4.172" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||||
|
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
|
||||||
|
<PackageReference Include="System.Management" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Proto\Proto.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Data\Models\Dispatch\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
84
Common/Configuration/ConfigContainer.cs
Normal file
84
Common/Configuration/ConfigContainer.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
namespace KianaBH.Configuration;
|
||||||
|
|
||||||
|
public class ConfigContainer
|
||||||
|
{
|
||||||
|
public HttpServerConfig HttpServer { get; set; } = new();
|
||||||
|
public GameServerConfig GameServer { get; set; } = new();
|
||||||
|
public PathConfig Path { get; set; } = new();
|
||||||
|
public ServerOption ServerOption { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HttpServerConfig
|
||||||
|
{
|
||||||
|
public string BindAddress { get; set; } = "0.0.0.0";
|
||||||
|
public string PublicAddress { get; set; } = "127.0.0.1";
|
||||||
|
public int Port { get; set; } = 80;
|
||||||
|
|
||||||
|
public string GetDisplayAddress()
|
||||||
|
{
|
||||||
|
return "http" + "://" + PublicAddress + ":" + Port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBindDisplayAddress()
|
||||||
|
{
|
||||||
|
return "http" + "://" + BindAddress + ":" + Port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameServerConfig
|
||||||
|
{
|
||||||
|
public string BindAddress { get; set; } = "0.0.0.0";
|
||||||
|
public string PublicAddress { get; set; } = "127.0.0.1";
|
||||||
|
public int Port { get; set; } = 21000;
|
||||||
|
public int KcpAliveMs { get; set; } = 45000;
|
||||||
|
public string DatabaseName { get; set; } = "kiana.db";
|
||||||
|
public string GameServerId { get; set; } = "KianaBH";
|
||||||
|
public string GameServerName { get; set; } = "KianaBH";
|
||||||
|
public string GetDisplayAddress()
|
||||||
|
{
|
||||||
|
return PublicAddress + ":" + Port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PathConfig
|
||||||
|
{
|
||||||
|
public string ResourcePath { get; set; } = "Resources";
|
||||||
|
public string ConfigPath { get; set; } = "Config";
|
||||||
|
public string DatabasePath { get; set; } = "Config/Database";
|
||||||
|
public string HandbookPath { get; set; } = "Config/Handbook";
|
||||||
|
public string LogPath { get; set; } = "Config/Logs";
|
||||||
|
public string DataPath { get; set; } = "Config/Data";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServerOption
|
||||||
|
{
|
||||||
|
public bool EnableMission { get; set; } = false;
|
||||||
|
public string DefaultGender { get; set; } = "Woman";
|
||||||
|
public string Language { get; set; } = "EN";
|
||||||
|
public string FallbackLanguage { get; set; } = "EN";
|
||||||
|
public string[] DefaultPermissions { get; set; } = ["Admin"];
|
||||||
|
public ServerProfile ServerProfile { get; set; } = new();
|
||||||
|
public bool AutoCreateUser { get; set; } = true;
|
||||||
|
public bool SavePersonalDebugFile { get; set; } = false;
|
||||||
|
public bool AutoSendResponseWhenNoHandler { get; set; } = true;
|
||||||
|
#if DEBUG
|
||||||
|
public bool EnableDebug { get; set; } = true;
|
||||||
|
#else
|
||||||
|
public bool EnableDebug { get; set; } = false;
|
||||||
|
#endif
|
||||||
|
public bool DebugMessage { get; set; } = true;
|
||||||
|
public bool DebugDetailMessage { get; set; } = true;
|
||||||
|
public bool DebugNoHandlerPacket { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServerProfile
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "Server";
|
||||||
|
public int Uid { get; set; } = 80;
|
||||||
|
public string Signature { get; set; } = "Type /help for a list of commands";
|
||||||
|
public int Level { get; set; } = 1;
|
||||||
|
public int HeadIcon { get; set; } = 200105;
|
||||||
|
public int ChatBubbleId { get; set; } = 220001;
|
||||||
|
public int DisplayAvatarId { get; set; } = 1001;
|
||||||
|
public int DisplayAvatarLevel { get; set; } = 1;
|
||||||
|
}
|
||||||
75
Common/Configuration/HotfixContainer.cs
Normal file
75
Common/Configuration/HotfixContainer.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace KianaBH.Configuration;
|
||||||
|
|
||||||
|
public class HotfixContainer
|
||||||
|
{
|
||||||
|
public bool UseLocalCache { get; set; } = false;
|
||||||
|
public Dictionary<string, HotfixManfiset> Hotfixes { get; set; } = new();
|
||||||
|
public Dictionary<string, string> AesKeys { get; set; } = new ();
|
||||||
|
|
||||||
|
public static string ExtractVersionNumber(string? version)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return version == null ? "" : version[..version.IndexOf('_')];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HotfixManfiset
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Asb")] public AsbData Asb { get; set; } = new();
|
||||||
|
[JsonPropertyName("AsbPreDownload")] public AsbPreDownloadData AsbPreDownload { get; set; } = new();
|
||||||
|
[JsonPropertyName("Audio")] public AudioData Audio { get; set; } = new();
|
||||||
|
[JsonPropertyName("AudioPreDownload")] public AudioPreDownloadData AudioPreDownload { get; set; } = new();
|
||||||
|
[JsonPropertyName("VideoEncrypt")] public VideoEncryptData VideoEncrypt { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AsbData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("android")] public PlatformInfo Android { get; set; } = new();
|
||||||
|
[JsonPropertyName("iphone")] public PlatformInfo Iphone { get; set; } = new();
|
||||||
|
[JsonPropertyName("pc")] public PlatformInfo Pc { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AsbPreDownloadData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("android")] public PlatformEncryptedInfo Android { get; set; } = new();
|
||||||
|
[JsonPropertyName("iphone")] public PlatformEncryptedInfo Iphone { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AudioData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("platform")] public Dictionary<string, string> Platform { get; set; } = new();
|
||||||
|
[JsonPropertyName("revision")] public int Revision { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AudioPreDownloadData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("enable_time")] public long EnableTime { get; set; }
|
||||||
|
[JsonPropertyName("platform")] public Dictionary<string, string> Platform { get; set; } = new();
|
||||||
|
[JsonPropertyName("revision")] public int Revision { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VideoEncryptData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("filename")] public string FileName { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlatformInfo
|
||||||
|
{
|
||||||
|
[JsonPropertyName("enable_time")] public long EnableTime { get; set; }
|
||||||
|
[JsonPropertyName("revision")] public string Revision { get; set; } = "";
|
||||||
|
[JsonPropertyName("suffix")] public string Suffix { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlatformEncryptedInfo : PlatformInfo
|
||||||
|
{
|
||||||
|
[JsonPropertyName("encrypt_key")] public string EncryptKey { get; set; } = "";
|
||||||
|
}
|
||||||
7
Common/Data/Config/TimeStampConfig.cs
Normal file
7
Common/Data/Config/TimeStampConfig.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace KianaBH.Data.Config;
|
||||||
|
|
||||||
|
public class TimestampConfig
|
||||||
|
{
|
||||||
|
public uint TimeStampForBakedReader { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
24
Common/Data/Excel/ActChallengeDataExcel.cs
Normal file
24
Common/Data/Excel/ActChallengeDataExcel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("ActChallengeData.json")]
|
||||||
|
public class ActChallengeDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("actId")] public uint ActId { get; set; }
|
||||||
|
[JsonPropertyName("difficulty")] public uint Difficulty { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)ActId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
if (!GameData.ActChallengeData.ContainsKey(GetId()))
|
||||||
|
{
|
||||||
|
GameData.ActChallengeData[GetId()] = new List<ActChallengeDataExcel>();
|
||||||
|
}
|
||||||
|
GameData.ActChallengeData[GetId()].Add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Common/Data/Excel/ActivityTowerExcel.cs
Normal file
17
Common/Data/Excel/ActivityTowerExcel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("ActivityTower.json")]
|
||||||
|
public class ActivityTowerExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint ActivityID { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)ActivityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.ActivityTowerData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Data/Excel/AffixListExcel.cs
Normal file
20
Common/Data/Excel/AffixListExcel.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("AffixList.json")]
|
||||||
|
public class AffixListExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("affixID")] public int AffixID { get; set; }
|
||||||
|
[JsonPropertyName("level")] public int Level { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return AffixID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.AffixListData.Add(AffixID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Common/Data/Excel/AvatarDataExcel.cs
Normal file
27
Common/Data/Excel/AvatarDataExcel.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("AvatarData.json")]
|
||||||
|
public class AvatarDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("avatarID")] public int AvatarID { get; set; }
|
||||||
|
[JsonPropertyName("unlockStar")] public int UnlockStar { get; set; }
|
||||||
|
[JsonPropertyName("initialWeapon")] public int InitialWeapon { get; set; }
|
||||||
|
[JsonPropertyName("skillList")] public List<int> SkillList { get; set; } = [];
|
||||||
|
public int DefaultDressId { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return AvatarID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
if (AvatarID != 316 && (AvatarID < 9000 || AvatarID > 20000))
|
||||||
|
{
|
||||||
|
GameData.AvatarData.Add(AvatarID, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Common/Data/Excel/AvatarSubSkillDataExcel.cs
Normal file
22
Common/Data/Excel/AvatarSubSkillDataExcel.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("AvatarSubSkillData.json")]
|
||||||
|
public class AvatarSubSkillDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("skillId")] public int SkillId { get; set; }
|
||||||
|
[JsonPropertyName("unlockScoin")] public int UnlockScoin { get; set; }
|
||||||
|
[JsonPropertyName("maxLv")] public int MaxLv { get; set; }
|
||||||
|
[JsonPropertyName("avatarSubSkillId")] public int AvatarSubSkillId { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return AvatarSubSkillId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.AvatarSubSkillData.Add(AvatarSubSkillId, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Common/Data/Excel/AvatarTutorialExcel.cs
Normal file
17
Common/Data/Excel/AvatarTutorialExcel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("AvatarTutorial.json")]
|
||||||
|
public class AvatarTutorialExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint ActivityID { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)ActivityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.AvatarTutorialData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Common/Data/Excel/ChapterGroupConfigExcel.cs
Normal file
25
Common/Data/Excel/ChapterGroupConfigExcel.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using KianaBH.Data.Config;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("ChapterGroupConfig.json")]
|
||||||
|
public class ChapterGroupConfigExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint ID { get; set; }
|
||||||
|
public uint GroupType { get; set; }
|
||||||
|
public TimestampConfig? BeginShowTime { get; set; }
|
||||||
|
public TimestampConfig? BeginTime { get; set; }
|
||||||
|
public uint BeginShowLevel { get; set; }
|
||||||
|
public List<uint> SiteList { get; set; } = [];
|
||||||
|
public uint UnlockLevel { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.ChapterGroupConfigData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Common/Data/Excel/CityEventPhotoExcel.cs
Normal file
21
Common/Data/Excel/CityEventPhotoExcel.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using KianaBH.Data.Config;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("CityEventPhoto.json")]
|
||||||
|
public class CityEventPhotoExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint PhotoID { get; set; }
|
||||||
|
[JsonPropertyName("photoType")] public uint PhotoType { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)PhotoID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.CityEventPhotoData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Common/Data/Excel/CollectionExcel.cs
Normal file
17
Common/Data/Excel/CollectionExcel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("Collection.json")]
|
||||||
|
public class CollectionExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.CollectionData.Add(ID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Common/Data/Excel/CustomHeadDataExcel.cs
Normal file
19
Common/Data/Excel/CustomHeadDataExcel.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("CustomHeadData.json")]
|
||||||
|
public class CustomHeadDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("headID")] public uint HeadID { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)HeadID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.CustomHeadData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Data/Excel/DressDataExcel.cs
Normal file
20
Common/Data/Excel/DressDataExcel.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("DressData.json")]
|
||||||
|
public class DressDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("dressID")] public uint DressID { get; set; }
|
||||||
|
[JsonPropertyName("avatarIDList")] public List<uint> AvatarIDList { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)DressID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.DressData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Common/Data/Excel/ElfAstraMateDataExcel.cs
Normal file
30
Common/Data/Excel/ElfAstraMateDataExcel.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("Elf_AstraMate_Data.json")]
|
||||||
|
public class ElfAstraMateDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint ElfID { get; set; }
|
||||||
|
public uint MaxLevel { get; set; }
|
||||||
|
public uint MaxRarity { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore] public List<ElfSkillDataExcel> SkillList = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)ElfID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.ElfAstraMateData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AfterAllDone()
|
||||||
|
{
|
||||||
|
GameData.ElfSkillData.TryGetValue(GetId(), out var Skills);
|
||||||
|
if (Skills == null || !Skills.ElfIds.Contains(ElfID)) return;
|
||||||
|
SkillList.Add(Skills);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Common/Data/Excel/ElfSkillDataExcel.cs
Normal file
18
Common/Data/Excel/ElfSkillDataExcel.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("ElfSkillData.json")]
|
||||||
|
public class ElfSkillDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint ElfSkillID { get; set; }
|
||||||
|
public uint MaxLv { get; set; }
|
||||||
|
public List<uint> ElfIds { get; set; } = [];
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)ElfSkillID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.ElfSkillData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Common/Data/Excel/EntryThemeDataExcel.cs
Normal file
18
Common/Data/Excel/EntryThemeDataExcel.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("EntryThemeData.json")]
|
||||||
|
public class EntryThemeDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint SpaceShipConfigId { get; set; }
|
||||||
|
public List<uint> ThemeBgmConfigList { get; set; } = [];
|
||||||
|
public List<uint> ThemeTagList { get; set; } = [];
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)SpaceShipConfigId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.EntryThemeData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Common/Data/Excel/EntryThemeItemDataExcel.cs
Normal file
16
Common/Data/Excel/EntryThemeItemDataExcel.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("EntryThemeItemData.json")]
|
||||||
|
public class EntryThemeItemDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int ThemeItemID { get; set; }
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return ThemeItemID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.EntryThemeItemData.Add(ThemeItemID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Common/Data/Excel/FrameDataExcel.cs
Normal file
19
Common/Data/Excel/FrameDataExcel.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("FrameData.json")]
|
||||||
|
public class FrameDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")] public uint Id { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.FrameData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Common/Data/Excel/GeneralActivityExcel.cs
Normal file
18
Common/Data/Excel/GeneralActivityExcel.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GeneralActivity.json")]
|
||||||
|
public class GeneralActivityExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint AcitivityID { get; set; }
|
||||||
|
public uint Series { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)AcitivityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GeneralActivityData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Common/Data/Excel/GeneralActivityStageGroupExcel.cs
Normal file
24
Common/Data/Excel/GeneralActivityStageGroupExcel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GeneralActivityStageGroup.json")]
|
||||||
|
public class GeneralActivityStageGroupExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint AcitivityId { get; set; }
|
||||||
|
public uint StageGroupId { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)AcitivityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
if (!GameData.GeneralActivityStageGroupData.ContainsKey(GetId()))
|
||||||
|
{
|
||||||
|
GameData.GeneralActivityStageGroupData[GetId()] = new List<GeneralActivityStageGroupExcel>();
|
||||||
|
}
|
||||||
|
GameData.GeneralActivityStageGroupData[GetId()].Add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Data/Excel/GodWarEventExcel.cs
Normal file
20
Common/Data/Excel/GodWarEventExcel.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GodWarEvent.json")]
|
||||||
|
public class GodWarEventExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint EventID { get; set; }
|
||||||
|
public int EventType { get; set; }
|
||||||
|
public List<uint> ParamsVar { get; set; } = [];
|
||||||
|
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)EventID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GodWarEventData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Common/Data/Excel/GodWarMainAvatarExcel.cs
Normal file
17
Common/Data/Excel/GodWarMainAvatarExcel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GodWarMainAvatar.json")]
|
||||||
|
public class GodWarMainAvatarExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int MainAvatarID { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return MainAvatarID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GodWarMainAvatarData.Add(MainAvatarID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Data/Excel/GodWarRelationDataExcel.cs
Normal file
20
Common/Data/Excel/GodWarRelationDataExcel.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GodWarRelationData.json")]
|
||||||
|
public class GodWarRelationDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int AvatarID { get; set; }
|
||||||
|
public int RoleID { get; set; }
|
||||||
|
public int Level { get; set; }
|
||||||
|
public int MaxLevel { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return AvatarID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GodWarRelationData.Add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Common/Data/Excel/GodWarSupportAvatarExcel.cs
Normal file
17
Common/Data/Excel/GodWarSupportAvatarExcel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GodWarSupportAvatar.json")]
|
||||||
|
public class GodWarSupportAvatarExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int SupportAvatarID { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return SupportAvatarID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GodWarSupportAvatarData.Add(SupportAvatarID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Common/Data/Excel/GodWarTaleScheduleExcel.cs
Normal file
22
Common/Data/Excel/GodWarTaleScheduleExcel.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using KianaBH.Data.Config;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GodWarTaleSchedule.json")]
|
||||||
|
public class GodWarTaleScheduleExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint TaleScheduleID { get; set; }
|
||||||
|
public List<uint> TaleIDList { get; set; } = [];
|
||||||
|
public TimestampConfig? BeginTime { get; set; }
|
||||||
|
public TimestampConfig? EndTime { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)TaleScheduleID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GodWarTaleScheduleData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Common/Data/Excel/GodWarTalentDataExcel.cs
Normal file
19
Common/Data/Excel/GodWarTalentDataExcel.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("GodWarTalentData.json")]
|
||||||
|
public class GodWarTalentDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint TalentID { get; set; }
|
||||||
|
public uint MaxLevel { get; set; }
|
||||||
|
public List<uint> TaleIDList { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)TalentID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GodWarTalentData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Common/Data/Excel/MaterialDataExcel.cs
Normal file
22
Common/Data/Excel/MaterialDataExcel.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("MaterialData.json")]
|
||||||
|
public class MaterialDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("ID")] public int Id { get; set; }
|
||||||
|
[JsonPropertyName("rarity")] public int Rarity { get; set; }
|
||||||
|
[JsonPropertyName("maxRarity")] public int MaxRarity { get; set; }
|
||||||
|
[JsonPropertyName("quantityLimit")] public int QuantityLimit { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.MaterialData.Add(Id, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Common/Data/Excel/MissionDataExcel.cs
Normal file
22
Common/Data/Excel/MissionDataExcel.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("MissionData.json")]
|
||||||
|
public class MissionDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")] public uint Id { get; set; }
|
||||||
|
[JsonPropertyName("totalProgress")] public uint TotalProgress { get; set; }
|
||||||
|
public uint Priority { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.MissionData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Data/Excel/PhonePendantDataExcel.cs
Normal file
20
Common/Data/Excel/PhonePendantDataExcel.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("PhonePendantData.json")]
|
||||||
|
public class PhonePendantDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint PendantId { get; set; }
|
||||||
|
public uint Rarity { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)PendantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.PhonePendantData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Common/Data/Excel/RandomPlotDataExcel.cs
Normal file
21
Common/Data/Excel/RandomPlotDataExcel.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("RandomPlotData.json")]
|
||||||
|
public class RandomPlotDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("plotId")] public uint PlotId { get; set; }
|
||||||
|
[JsonPropertyName("startDialogId")] public uint StartDialogId { get; set; }
|
||||||
|
[JsonPropertyName("finishDialogIdList")] public List<uint> FinishDialogIdList { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)PlotId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.RandomPlotData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Common/Data/Excel/RecommendPanelExcel.cs
Normal file
17
Common/Data/Excel/RecommendPanelExcel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("RecommendPanel.json")]
|
||||||
|
public class RecommendPanelExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint PanelId { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)PanelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.RecommendPanelData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
Common/Data/Excel/StageDataMainExcel.cs
Normal file
28
Common/Data/Excel/StageDataMainExcel.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("StageData_Main.json")]
|
||||||
|
public class StageDataMainExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("levelId")] public uint LevelId { get; set; }
|
||||||
|
[JsonPropertyName("maxProgress")] public uint MaxProgress { get; set; }
|
||||||
|
[JsonPropertyName("challengeList")] public List<ChallengeData> ChallengeList { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)LevelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.StageDataMain.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ChallengeData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("challengeId")] public uint ChallengeId { get; set; }
|
||||||
|
[JsonPropertyName("rewardId")] public uint RewardId { get; set; }
|
||||||
|
}
|
||||||
21
Common/Data/Excel/StepMissionCompensationExcel.cs
Normal file
21
Common/Data/Excel/StepMissionCompensationExcel.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("StepMissionCompensation.json")]
|
||||||
|
public class StepMissionCompensationExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint CompensationId { get; set; }
|
||||||
|
public uint UnlockLevel { get; set; }
|
||||||
|
public List<uint> MainLineStepIdList { get; set; } = [];
|
||||||
|
public List<uint> NewChallengeStepIdList { get; set; } = [];
|
||||||
|
public List<uint> OldChallengeStepIdList { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)CompensationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.StepMissionCompensationData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Common/Data/Excel/StigmataDataExcel.cs
Normal file
26
Common/Data/Excel/StigmataDataExcel.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("StigmataData.json")]
|
||||||
|
public class StigmataDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int ID { get; set; }
|
||||||
|
[JsonPropertyName("maxLv")] public int MaxLv { get; set; }
|
||||||
|
[JsonPropertyName("rarity")] public int Rarity { get; set; }
|
||||||
|
[JsonPropertyName("maxRarity")] public int MaxRarity { get; set; }
|
||||||
|
[JsonPropertyName("evoID")] public int EvoID { get; set; }
|
||||||
|
[JsonPropertyName("quality")] public int Quality { get; set; }
|
||||||
|
[JsonPropertyName("isSecurityProtect")] public bool IsSecurityProtect { get; set; }
|
||||||
|
[JsonPropertyName("protect")] public bool Protect { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.StigmataData.Add(ID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Common/Data/Excel/ThemeDataAvatarExcel.cs
Normal file
21
Common/Data/Excel/ThemeDataAvatarExcel.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("ThemeData_Avatar.json")]
|
||||||
|
public class ThemeDataAvatarExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint AvatarData { get; set; }
|
||||||
|
public List<uint> BuffList { get; set; } = [];
|
||||||
|
[JsonPropertyName("avatarIDList")] public List<uint> AvatarIDList { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)AvatarData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.ThemeDataAvatar.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Data/Excel/TutorialDataExcel.cs
Normal file
20
Common/Data/Excel/TutorialDataExcel.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("TutorialData.json")]
|
||||||
|
public class TutorialDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")] public uint Id { get; set; }
|
||||||
|
[JsonPropertyName("index")] public uint Index { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return (int)Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.TutorialData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Common/Data/Excel/WeaponDataExcel.cs
Normal file
25
Common/Data/Excel/WeaponDataExcel.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("WeaponData.json")]
|
||||||
|
public class WeaponDataExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public int ID { get; set; }
|
||||||
|
[JsonPropertyName("weaponMainID")] public int WeaponMainID { get; set; }
|
||||||
|
[JsonPropertyName("maxLv")] public int MaxLv { get; set; }
|
||||||
|
[JsonPropertyName("rarity")] public int Rarity { get; set; }
|
||||||
|
[JsonPropertyName("maxRarity")] public int MaxRarity { get; set; }
|
||||||
|
[JsonPropertyName("evoID")] public int EvoID { get; set; }
|
||||||
|
[JsonPropertyName("protect")] public bool Protect { get; set; }
|
||||||
|
|
||||||
|
public override int GetId()
|
||||||
|
{
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.WeaponData.Add(ID, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Common/Data/ExcelResource.cs
Normal file
18
Common/Data/ExcelResource.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace KianaBH.Data;
|
||||||
|
|
||||||
|
public abstract class ExcelResource
|
||||||
|
{
|
||||||
|
public abstract int GetId();
|
||||||
|
|
||||||
|
public virtual void Loaded()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Finalized()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void AfterAllDone()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
45
Common/Data/GameData.cs
Normal file
45
Common/Data/GameData.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using KianaBH.Data.Excel;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Data;
|
||||||
|
|
||||||
|
public static class GameData
|
||||||
|
{
|
||||||
|
public static Dictionary<int, List<ActChallengeDataExcel>> ActChallengeData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, ActivityTowerExcel> ActivityTowerData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, AffixListExcel> AffixListData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, AvatarDataExcel> AvatarData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, AvatarSubSkillDataExcel> AvatarSubSkillData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, AvatarTutorialExcel> AvatarTutorialData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, CollectionExcel> CollectionData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, CustomHeadDataExcel> CustomHeadData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, DressDataExcel> DressData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, ElfAstraMateDataExcel> ElfAstraMateData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, ElfSkillDataExcel> ElfSkillData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, EntryThemeDataExcel> EntryThemeData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, EntryThemeItemDataExcel> EntryThemeItemData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, FrameDataExcel> FrameData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, List<GeneralActivityStageGroupExcel>> GeneralActivityStageGroupData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, GeneralActivityExcel> GeneralActivityData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, GodWarEventExcel> GodWarEventData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, GodWarMainAvatarExcel> GodWarMainAvatarData { get; private set; } = [];
|
||||||
|
public static List<GodWarRelationDataExcel> GodWarRelationData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, GodWarSupportAvatarExcel> GodWarSupportAvatarData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, GodWarTaleScheduleExcel> GodWarTaleScheduleData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, GodWarTalentDataExcel> GodWarTalentData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, MaterialDataExcel> MaterialData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, MissionDataExcel> MissionData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, RecommendPanelExcel> RecommendPanelData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, StageDataMainExcel> StageDataMain { get; private set; } = [];
|
||||||
|
public static Dictionary<int, StepMissionCompensationExcel> StepMissionCompensationData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, StigmataDataExcel> StigmataData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, ThemeDataAvatarExcel> ThemeDataAvatar { get; private set; } = [];
|
||||||
|
public static Dictionary<int, WeaponDataExcel> WeaponData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, ChapterGroupConfigExcel> ChapterGroupConfigData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, PhonePendantDataExcel> PhonePendantData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, TutorialDataExcel> TutorialData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, CityEventPhotoExcel> CityEventPhotoData { get; private set; } = [];
|
||||||
|
public static Dictionary<int, RandomPlotDataExcel> RandomPlotData { get; private set; } = [];
|
||||||
|
}
|
||||||
13
Common/Data/Models/Dispatch/DispatchQuery.cs
Normal file
13
Common/Data/Models/Dispatch/DispatchQuery.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Models.Dispatch;
|
||||||
|
|
||||||
|
public class DispatchQuery
|
||||||
|
{
|
||||||
|
[Required] public string? Version { get; set; }
|
||||||
|
[FromQuery(Name = "t")] public int Timestamp { get; set; }
|
||||||
|
public string? Lang { get; set; }
|
||||||
|
public int Uid { get; set; }
|
||||||
|
public string? Token { get; set; }
|
||||||
|
}
|
||||||
17
Common/Data/Models/Dispatch/QueryDispatchModels.cs
Normal file
17
Common/Data/Models/Dispatch/QueryDispatchModels.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace KianaBH.Data.Models.Dispatch;
|
||||||
|
|
||||||
|
public class QueryDispatchResponse
|
||||||
|
{
|
||||||
|
public int Retcode { get; set; }
|
||||||
|
public List<RegionInfo> RegionList { get; set; } = [];
|
||||||
|
|
||||||
|
|
||||||
|
public class RegionInfo
|
||||||
|
{
|
||||||
|
public string? DispatchUrl { get; set; }
|
||||||
|
public object? Ext { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public int Retcode { get; set; }
|
||||||
|
public string? Title { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Common/Data/Models/Dispatch/QueryGatewayModels.cs
Normal file
30
Common/Data/Models/Dispatch/QueryGatewayModels.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
using KianaBH.Configuration;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Models.Dispatch;
|
||||||
|
|
||||||
|
public class QueryGatewayResponse
|
||||||
|
{
|
||||||
|
public long ServerCurTime { get; set; } = Extensions.GetUnixSec();
|
||||||
|
public int ServerCurTimezone { get; set; } = (int)TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).TotalHours;
|
||||||
|
public string RegionName { get; set; } = "KianaBH";
|
||||||
|
public string Msg { get; set; } = "";
|
||||||
|
public bool IsDataReady { get; set; } = true;
|
||||||
|
public int Retcode { get; set; }
|
||||||
|
|
||||||
|
public string? AccountUrl { get; set; }
|
||||||
|
public ServerInfo? Gameserver { get; set; }
|
||||||
|
public ServerInfo? Gateway { get; set; }
|
||||||
|
public List<string> ExResourceUrlList { get; set; } = [];
|
||||||
|
public List<string> ExAudioAndVideoUrlList { get; set; } = [];
|
||||||
|
public List<string> AssetBundleUrlList { get; set; } = [];
|
||||||
|
public HotfixManfiset? Manifest { get; set; }
|
||||||
|
public Dictionary<string, object> Ext { get; set; } = new();
|
||||||
|
|
||||||
|
public class ServerInfo
|
||||||
|
{
|
||||||
|
public string? Ip { get; set; }
|
||||||
|
public bool IsKcp { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
34
Common/Data/Models/Sdk/ComboGranterModels.cs
Normal file
34
Common/Data/Models/Sdk/ComboGranterModels.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
|
||||||
|
namespace KianaBH.Data.Models.Sdk;
|
||||||
|
|
||||||
|
public class ComboGranterData
|
||||||
|
{
|
||||||
|
public string? Uid { get; set; }
|
||||||
|
public string? Token { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ComboGranterRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[JsonConverter(typeof(JsonStringToObjectConverter<ComboGranterData>))]
|
||||||
|
public ComboGranterData? Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ComboGranterResponse : ResponseBase
|
||||||
|
{
|
||||||
|
public new ComboGranterResponseData? Data { get; set; }
|
||||||
|
|
||||||
|
public class ComboGranterResponseData
|
||||||
|
{
|
||||||
|
public uint AccountType { get; set; }
|
||||||
|
public string? OpenId { get; set; }
|
||||||
|
public string? ComboId { get; set; }
|
||||||
|
public string? ComboToken { get; set; }
|
||||||
|
public bool Heartbeat { get; set; }
|
||||||
|
public string? Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
6
Common/Data/Models/Sdk/DeviceFingerprintController.cs
Normal file
6
Common/Data/Models/Sdk/DeviceFingerprintController.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace KianaBH.Data.Models.Sdk;
|
||||||
|
public class GetDeviceFingerprintRequest
|
||||||
|
{
|
||||||
|
public string? DeviceFp { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
20
Common/Data/Models/Sdk/GameWeatherModels.cs
Normal file
20
Common/Data/Models/Sdk/GameWeatherModels.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace KianaBH.Data.Models.Sdk;
|
||||||
|
|
||||||
|
public class GetWeatherResponse : ResponseBase
|
||||||
|
{
|
||||||
|
public new GetWeatherResponseData? Data { get; set; }
|
||||||
|
|
||||||
|
public class GetWeatherResponseData
|
||||||
|
{
|
||||||
|
public int Timezone { get; set; }
|
||||||
|
public List<HourlyWeatherData> Hourly { get; set; } = [];
|
||||||
|
|
||||||
|
public class HourlyWeatherData
|
||||||
|
{
|
||||||
|
public int Condition { get; set; }
|
||||||
|
public int Hour { get; set; }
|
||||||
|
public string? Date { get; set; }
|
||||||
|
public int Temp { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Common/Data/Models/Sdk/MdkShieldModels.cs
Normal file
42
Common/Data/Models/Sdk/MdkShieldModels.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
namespace KianaBH.Data.Models.Sdk;
|
||||||
|
|
||||||
|
public class MdkShieldLoginRequest
|
||||||
|
{
|
||||||
|
public string? Account { get; set; }
|
||||||
|
public string? Password { get; set; }
|
||||||
|
public bool IsCrypto { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MdkShieldVerifyRequest
|
||||||
|
{
|
||||||
|
public string? Uid { get; set; }
|
||||||
|
public string? Token { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move this to DB instead
|
||||||
|
public class MdkShieldAccountData
|
||||||
|
{
|
||||||
|
public string? Token { get; set; }
|
||||||
|
public string? Uid { get; set; }
|
||||||
|
|
||||||
|
public string Email { get; set; } = "";
|
||||||
|
public string IsEmailVerify { get; set; } = "0";
|
||||||
|
public string AreaCode { get; set; } = "";
|
||||||
|
public string Country { get; set; } = "";
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public string Realname { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MdkShieldResponse : ResponseBase
|
||||||
|
{
|
||||||
|
public new MdkShieldResponseData? Data { get; set; }
|
||||||
|
|
||||||
|
public class MdkShieldResponseData
|
||||||
|
{
|
||||||
|
public MdkShieldAccountData? Account { get; set; }
|
||||||
|
public bool DeviceGrantRequired { get; set; }
|
||||||
|
public bool ReactiveRequired { get; set; }
|
||||||
|
public bool RealpersonRequired { get; set; }
|
||||||
|
public bool SafeMobileRequeired { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Common/Data/Models/Sdk/ResponseBase.cs
Normal file
9
Common/Data/Models/Sdk/ResponseBase.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace KianaBH.Data.Models.Sdk;
|
||||||
|
|
||||||
|
public class ResponseBase
|
||||||
|
{
|
||||||
|
public string Message { get; set; } = "OK";
|
||||||
|
public bool Success { get; set; } = true;
|
||||||
|
public int Retcode { get; set; }
|
||||||
|
public object? Data { get; set; }
|
||||||
|
}
|
||||||
33
Common/Data/ResourceEntity.cs
Normal file
33
Common/Data/ResourceEntity.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace KianaBH.Data;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public class ResourceEntity : Attribute
|
||||||
|
{
|
||||||
|
[Obsolete("No effect")]
|
||||||
|
public ResourceEntity(string fileName, bool isCritical = false, bool isMultifile = false)
|
||||||
|
{
|
||||||
|
if (isMultifile)
|
||||||
|
FileName = new List<string>(fileName.Split(','));
|
||||||
|
else
|
||||||
|
FileName = [fileName];
|
||||||
|
IsCritical = isCritical;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ResourceEntity(string fileName, bool isMultifile = false)
|
||||||
|
{
|
||||||
|
if (isMultifile)
|
||||||
|
FileName = new List<string>(fileName.Split(','));
|
||||||
|
else
|
||||||
|
FileName = [fileName];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceEntity(string fileName)
|
||||||
|
{
|
||||||
|
FileName = [fileName];
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> FileName { get; private set; }
|
||||||
|
|
||||||
|
[Obsolete("No effect")] public bool IsCritical { get; private set; } // deprecated
|
||||||
|
}
|
||||||
169
Common/Data/ResourceManager.cs
Normal file
169
Common/Data/ResourceManager.cs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
using KianaBH.Internationalization;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace KianaBH.Data;
|
||||||
|
|
||||||
|
public class ResourceManager
|
||||||
|
{
|
||||||
|
public static Logger Logger { get; } = new("ResourceManager");
|
||||||
|
public static bool IsLoaded { get; set; }
|
||||||
|
|
||||||
|
public static void LoadGameData()
|
||||||
|
{
|
||||||
|
LoadExcel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadExcel()
|
||||||
|
{
|
||||||
|
var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly
|
||||||
|
List<ExcelResource> resList = [];
|
||||||
|
|
||||||
|
foreach (var cls in classes.Where(x => x.IsSubclassOf(typeof(ExcelResource))))
|
||||||
|
{
|
||||||
|
var res = LoadSingleExcelResource(cls);
|
||||||
|
if (res != null) resList.AddRange(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var cls in resList) cls.AfterAllDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<T>? LoadSingleExcel<T>(Type cls) where T : ExcelResource, new()
|
||||||
|
{
|
||||||
|
return LoadSingleExcelResource(cls) as List<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ExcelResource>? LoadSingleExcelResource(Type cls)
|
||||||
|
{
|
||||||
|
var attribute = (ResourceEntity?)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity));
|
||||||
|
|
||||||
|
if (attribute == null) return null;
|
||||||
|
var resource = (ExcelResource)Activator.CreateInstance(cls)!;
|
||||||
|
var count = 0;
|
||||||
|
List<ExcelResource> resList = [];
|
||||||
|
foreach (var fileName in attribute.FileName)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var path = ConfigManager.Config.Path.ResourcePath + "/ExcelOutput/" + fileName;
|
||||||
|
var file = new FileInfo(path);
|
||||||
|
if (!file.Exists)
|
||||||
|
{
|
||||||
|
Logger.Error(I18NManager.Translate("Server.ServerInfo.FailedToReadItem", fileName,
|
||||||
|
I18NManager.Translate("Word.NotFound")));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = file.OpenText().ReadToEnd();
|
||||||
|
using (var reader = new JsonTextReader(new StringReader(json)))
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
switch (reader.TokenType)
|
||||||
|
{
|
||||||
|
case JsonToken.StartArray:
|
||||||
|
{
|
||||||
|
// array
|
||||||
|
var jArray = JArray.Parse(json);
|
||||||
|
foreach (var item in jArray)
|
||||||
|
{
|
||||||
|
var res = JsonConvert.DeserializeObject(item.ToString(), cls);
|
||||||
|
resList.Add((ExcelResource)res!);
|
||||||
|
((ExcelResource?)res)?.Loaded();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case JsonToken.StartObject:
|
||||||
|
{
|
||||||
|
// dictionary
|
||||||
|
var jObject = JObject.Parse(json);
|
||||||
|
foreach (var (_, obj) in jObject)
|
||||||
|
{
|
||||||
|
var instance = JsonConvert.DeserializeObject(obj!.ToString(), cls);
|
||||||
|
|
||||||
|
if (((ExcelResource?)instance)?.GetId() == 0 || (ExcelResource?)instance == null)
|
||||||
|
{
|
||||||
|
// Deserialize as JObject to handle nested dictionaries
|
||||||
|
var nestedObject = JsonConvert.DeserializeObject<JObject>(obj.ToString());
|
||||||
|
|
||||||
|
foreach (var nestedItem in nestedObject ?? [])
|
||||||
|
{
|
||||||
|
var nestedInstance =
|
||||||
|
JsonConvert.DeserializeObject(nestedItem.Value!.ToString(), cls);
|
||||||
|
resList.Add((ExcelResource)nestedInstance!);
|
||||||
|
((ExcelResource?)nestedInstance)?.Loaded();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resList.Add((ExcelResource)instance);
|
||||||
|
((ExcelResource)instance).Loaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Finalized();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error(
|
||||||
|
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", fileName,
|
||||||
|
I18NManager.Translate("Word.Error")), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", count.ToString(), cls.Name));
|
||||||
|
|
||||||
|
return resList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? LoadCustomFile<T>(string filetype, string filename)
|
||||||
|
{
|
||||||
|
var type = I18NManager.Translate("Word." + filetype);
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", type));
|
||||||
|
FileInfo file = new(ConfigManager.Config.Path.DataPath + $"/{filename}.json");
|
||||||
|
T? customFile = default;
|
||||||
|
if (!file.Exists)
|
||||||
|
{
|
||||||
|
Logger.Warn(I18NManager.Translate("Server.ServerInfo.ConfigMissing", type,
|
||||||
|
$"{ConfigManager.Config.Path.DataPath}/{filename}.json", type));
|
||||||
|
return customFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var reader = file.OpenRead();
|
||||||
|
using StreamReader reader2 = new(reader);
|
||||||
|
var text = reader2.ReadToEnd();
|
||||||
|
var json = JsonConvert.DeserializeObject<T>(text);
|
||||||
|
customFile = json;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error("Error in reading " + file.Name, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (customFile)
|
||||||
|
{
|
||||||
|
case Dictionary<int, int> d:
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", d.Count.ToString(), type));
|
||||||
|
break;
|
||||||
|
case Dictionary<int, List<int>> di:
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", di.Count.ToString(), type));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", filetype));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return customFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
150
Common/Database/Account/AccountData.cs
Normal file
150
Common/Database/Account/AccountData.cs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
using KianaBH.Enums.Player;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
using KianaBH.Util.Security;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Account;
|
||||||
|
|
||||||
|
[SugarTable("Account")]
|
||||||
|
public class AccountData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
public string Username { get; set; } = "";
|
||||||
|
public string Password { get; set; } = "";
|
||||||
|
public BanTypeEnum BanType { get; set; }
|
||||||
|
|
||||||
|
[SugarColumn(IsJson = true)] public List<PermEnum> Permissions { get; set; } = [];
|
||||||
|
|
||||||
|
[SugarColumn(IsNullable = true)] public string? ComboToken { get; set; }
|
||||||
|
|
||||||
|
#region GetAccount
|
||||||
|
|
||||||
|
public static AccountData? GetAccountByUserName(string username)
|
||||||
|
{
|
||||||
|
AccountData? result = null;
|
||||||
|
DatabaseHelper.GetAllInstance<AccountData>()?.ForEach(account =>
|
||||||
|
{
|
||||||
|
if (account.Username == username) result = account;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountData? GetAccountByUid(int uid, bool force = false)
|
||||||
|
{
|
||||||
|
var result = DatabaseHelper.GetInstance<AccountData>(uid, force);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Account
|
||||||
|
|
||||||
|
public static void CreateAccount(string username, int uid, string password)
|
||||||
|
{
|
||||||
|
var newUid = uid;
|
||||||
|
if (uid == 0)
|
||||||
|
{
|
||||||
|
newUid = 100001;
|
||||||
|
while (GetAccountByUid(newUid) != null) newUid++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var account = new AccountData
|
||||||
|
{
|
||||||
|
Uid = newUid,
|
||||||
|
Username = username,
|
||||||
|
Password = "",
|
||||||
|
Permissions = [.. ConfigManager.Config.ServerOption.DefaultPermissions
|
||||||
|
.Select(perm => Enum.TryParse(perm, out PermEnum result) ? result : (PermEnum?)null)
|
||||||
|
.Where(result => result.HasValue).Select(result => result!.Value)]
|
||||||
|
};
|
||||||
|
SetPassword(account, password);
|
||||||
|
|
||||||
|
DatabaseHelper.CreateInstance(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteAccount(int uid)
|
||||||
|
{
|
||||||
|
if (GetAccountByUid(uid) == null) return;
|
||||||
|
DatabaseHelper.DeleteAllInstance(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetPassword(AccountData account, string password)
|
||||||
|
{
|
||||||
|
if (password.Length > 0)
|
||||||
|
account.Password = Extensions.GetSha256Hash(password);
|
||||||
|
else
|
||||||
|
account.Password = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool VerifyPassword(AccountData account, string password)
|
||||||
|
=> account.Password == Extensions.GetSha256Hash(password);
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Permission
|
||||||
|
|
||||||
|
public static bool HasPerm(PermEnum[] perms, int uid)
|
||||||
|
{
|
||||||
|
if (uid == (int)ServerEnum.Console) return true;
|
||||||
|
var account = GetAccountByUid(uid);
|
||||||
|
if (account?.Permissions == null) return false;
|
||||||
|
if (account.Permissions.Contains(PermEnum.Admin)) return true;
|
||||||
|
|
||||||
|
return perms.Any(account.Permissions.Contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddPerm(PermEnum[] perms, int uid)
|
||||||
|
{
|
||||||
|
if (uid == (int)ServerEnum.Console) return;
|
||||||
|
var account = GetAccountByUid(uid);
|
||||||
|
if (account == null) return;
|
||||||
|
|
||||||
|
account.Permissions ??= [];
|
||||||
|
foreach (var perm in perms)
|
||||||
|
{
|
||||||
|
if (!account.Permissions.Contains(perm))
|
||||||
|
{
|
||||||
|
account.Permissions = [.. account.Permissions, perm];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemovePerm(PermEnum[] perms, int uid)
|
||||||
|
{
|
||||||
|
if (uid == (int)ServerEnum.Console) return;
|
||||||
|
var account = GetAccountByUid(uid);
|
||||||
|
if (account == null) return;
|
||||||
|
if (account.Permissions == null) return;
|
||||||
|
|
||||||
|
foreach (var perm in perms)
|
||||||
|
{
|
||||||
|
if (account.Permissions.Contains(perm))
|
||||||
|
{
|
||||||
|
account.Permissions = account.Permissions.Except([perm]).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CleanPerm(int uid)
|
||||||
|
{
|
||||||
|
if (uid == (int)ServerEnum.Console) return;
|
||||||
|
var account = GetAccountByUid(uid);
|
||||||
|
if (account == null) return;
|
||||||
|
|
||||||
|
account.Permissions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Token
|
||||||
|
|
||||||
|
public string GenerateComboToken()
|
||||||
|
{
|
||||||
|
ComboToken = Crypto.CreateSessionKey(Uid.ToString());
|
||||||
|
DatabaseHelper.UpdateInstance(this);
|
||||||
|
return ComboToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
95
Common/Database/Avatar/AvatarData.cs
Normal file
95
Common/Database/Avatar/AvatarData.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using SqlSugar;
|
||||||
|
using KianaBH.Proto;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Avatar;
|
||||||
|
|
||||||
|
[SugarTable("Avatar")]
|
||||||
|
public class AvatarData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsJson = true)] public List<AvatarInfo> Avatars { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AvatarInfo
|
||||||
|
{
|
||||||
|
public int AvatarId { get; set; }
|
||||||
|
public int Star { get; set; }
|
||||||
|
public int Level { get; set; }
|
||||||
|
public int Exp { get; set; }
|
||||||
|
public int Fragment { get; set; }
|
||||||
|
public int WeaponUniqueId { get; set; }
|
||||||
|
public int StigmataUniqueId1 { get; set; }
|
||||||
|
public int StigmataUniqueId2 { get; set; }
|
||||||
|
public int StigmataUniqueId3 { get; set; }
|
||||||
|
public List<AvatarSkill> SkillList { get; set; } = [];
|
||||||
|
public int TouchGoodFeel { get; set; }
|
||||||
|
public int TodayHasAddGoodFeel { get; set; }
|
||||||
|
public int StageGoodFeel { get; set; }
|
||||||
|
public List<int> DressList { get; set; } = [];
|
||||||
|
public int DressId { get; set; }
|
||||||
|
public AvatarBindEquipMode Mode { get; set; } = AvatarBindEquipMode.AvatarBindEquipCommon;
|
||||||
|
public AvatarArtifactDetail? AvatarArtifact { get; set; }
|
||||||
|
public int SubStar { get; set; }
|
||||||
|
public long Timestamp { get; set; }
|
||||||
|
public Proto.Avatar ToProto()
|
||||||
|
{
|
||||||
|
var proto = new Proto.Avatar
|
||||||
|
{
|
||||||
|
AvatarId = (uint)AvatarId,
|
||||||
|
Star = (uint)Star,
|
||||||
|
Level = (uint)Level,
|
||||||
|
Exp = (uint)Exp,
|
||||||
|
Fragment = (uint)Fragment,
|
||||||
|
WeaponUniqueId = (uint)WeaponUniqueId,
|
||||||
|
StigmataUniqueId1 = (uint)StigmataUniqueId1,
|
||||||
|
StigmataUniqueId2 = (uint)StigmataUniqueId2,
|
||||||
|
StigmataUniqueId3 = (uint)StigmataUniqueId3,
|
||||||
|
TouchGoodfeel = (uint)TouchGoodFeel,
|
||||||
|
TodayHasAddGoodfeel = (uint)TodayHasAddGoodFeel,
|
||||||
|
StageGoodfeel = (uint)StageGoodFeel,
|
||||||
|
DressId = (uint)DressId,
|
||||||
|
Mode = Mode,
|
||||||
|
SubStar = (uint)SubStar,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var dressId in DressList)
|
||||||
|
{
|
||||||
|
proto.DressList.Add((uint)dressId);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var skill in SkillList)
|
||||||
|
{
|
||||||
|
var avatarSkill = new Proto.AvatarSkill
|
||||||
|
{
|
||||||
|
SkillId = (uint)skill.SkillId
|
||||||
|
};
|
||||||
|
|
||||||
|
avatarSkill.SubSkillList.AddRange(skill.SubSkillList.Select(x => new Proto.AvatarSubSkill
|
||||||
|
{
|
||||||
|
SubSkillId = (uint)x.SubSkillId,
|
||||||
|
Level = x.Level,
|
||||||
|
IsMask = x.IsMask
|
||||||
|
}));
|
||||||
|
|
||||||
|
proto.SkillList.Add(avatarSkill);
|
||||||
|
}
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public class AvatarSkill
|
||||||
|
{
|
||||||
|
public int SkillId { get; set; }
|
||||||
|
public List<AvatarSubSkill> SubSkillList { get; set; } = [];
|
||||||
|
}
|
||||||
|
public class AvatarSubSkill
|
||||||
|
{
|
||||||
|
public int SubSkillId { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public bool IsMask { get; set; }
|
||||||
|
}
|
||||||
|
public class AvatarArtifactDetail
|
||||||
|
{
|
||||||
|
public int ArtifactId { get; set; }
|
||||||
|
public int ArtifactLevel { get; set; }
|
||||||
|
public bool IsArtifactSwitchOn { get; set; }
|
||||||
|
}
|
||||||
8
Common/Database/BaseDatabaseDataHelper.cs
Normal file
8
Common/Database/BaseDatabaseDataHelper.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database;
|
||||||
|
|
||||||
|
public abstract class BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsPrimaryKey = true)] public int Uid { get; set; }
|
||||||
|
}
|
||||||
28
Common/Database/Client/ClientData.cs
Normal file
28
Common/Database/Client/ClientData.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Google.Protobuf;
|
||||||
|
using KianaBH.Proto;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Client;
|
||||||
|
|
||||||
|
[SugarTable("client_data")]
|
||||||
|
public class ClientData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsJson = true)] public List<ClientDBData> Clients { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClientDBData
|
||||||
|
{
|
||||||
|
public uint Id { get; set; }
|
||||||
|
public ClientDataType Type { get; set; } = ClientDataType.ClientDataNone;
|
||||||
|
public byte[] Data { get; set; } = Array.Empty<byte>();
|
||||||
|
public Proto.ClientData ToProto()
|
||||||
|
{
|
||||||
|
var proto = new Proto.ClientData
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
Type = Type,
|
||||||
|
Data = ByteString.CopyFrom(Data)
|
||||||
|
};
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Common/Database/CustomSerializeService.cs
Normal file
32
Common/Database/CustomSerializeService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database;
|
||||||
|
|
||||||
|
public class CustomSerializeService : ISerializeService
|
||||||
|
{
|
||||||
|
private readonly JsonSerializerSettings _jsonSettings;
|
||||||
|
|
||||||
|
public CustomSerializeService()
|
||||||
|
{
|
||||||
|
_jsonSettings = new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
DefaultValueHandling = DefaultValueHandling.Ignore // ignore default values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SerializeObject(object value)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(value, _jsonSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T DeserializeObject<T>(string value)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(value)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SugarSerializeObject(object value)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(value, _jsonSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
307
Common/Database/DatabaseHelper.cs
Normal file
307
Common/Database/DatabaseHelper.cs
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
using KianaBH.Database.Account;
|
||||||
|
using KianaBH.Internationalization;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using SqlSugar;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace KianaBH.Database;
|
||||||
|
|
||||||
|
public class DatabaseHelper
|
||||||
|
{
|
||||||
|
public static Logger logger = new("Database");
|
||||||
|
public static SqlSugarScope? sqlSugarScope;
|
||||||
|
public static readonly ConcurrentDictionary<int, List<BaseDatabaseDataHelper>> UidInstanceMap = [];
|
||||||
|
public static readonly List<int> ToSaveUidList = [];
|
||||||
|
public static long LastSaveTick = DateTime.UtcNow.Ticks;
|
||||||
|
public static Thread? SaveThread;
|
||||||
|
public static bool LoadAccount;
|
||||||
|
public static bool LoadAllData;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.Database")));
|
||||||
|
var f = new FileInfo(ConfigManager.Config.Path.DatabasePath + "/" + ConfigManager.Config.GameServer.DatabaseName);
|
||||||
|
if (!f.Exists && f.Directory != null) f.Directory.Create();
|
||||||
|
|
||||||
|
sqlSugarScope = new SqlSugarScope(new ConnectionConfig
|
||||||
|
{
|
||||||
|
ConnectionString = $"Data Source={f.FullName};",
|
||||||
|
DbType = DbType.Sqlite,
|
||||||
|
IsAutoCloseConnection = true,
|
||||||
|
ConfigureExternalServices = new ConfigureExternalServices
|
||||||
|
{
|
||||||
|
SerializeService = new CustomSerializeService()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
InitializeSqlite();
|
||||||
|
|
||||||
|
var baseType = typeof(BaseDatabaseDataHelper);
|
||||||
|
var assembly = typeof(BaseDatabaseDataHelper).Assembly;
|
||||||
|
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
|
||||||
|
|
||||||
|
var list = sqlSugarScope.Queryable<AccountData>().ToList();
|
||||||
|
foreach (var inst in list)
|
||||||
|
{
|
||||||
|
if (!UidInstanceMap.TryGetValue(inst.Uid, out var value))
|
||||||
|
{
|
||||||
|
value = [];
|
||||||
|
UidInstanceMap[inst.Uid] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Add(inst); // add to the map
|
||||||
|
}
|
||||||
|
|
||||||
|
// start dispatch server
|
||||||
|
LoadAccount = true;
|
||||||
|
|
||||||
|
var res = Parallel.ForEach(list, account =>
|
||||||
|
{
|
||||||
|
Parallel.ForEach(types, t =>
|
||||||
|
{
|
||||||
|
if (t == typeof(AccountData)) return; // skip the account data
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
typeof(DatabaseHelper).GetMethod(nameof(InitializeTable))?.MakeGenericMethod(t)
|
||||||
|
.Invoke(null, [account.Uid]);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Database initialization error: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}); // cache the data
|
||||||
|
});
|
||||||
|
|
||||||
|
while (!res.IsCompleted)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LastSaveTick = DateTime.UtcNow.Ticks;
|
||||||
|
|
||||||
|
SaveThread = new Thread(() =>
|
||||||
|
{
|
||||||
|
while (true) CalcSaveDatabase();
|
||||||
|
});
|
||||||
|
SaveThread.Start();
|
||||||
|
|
||||||
|
LoadAllData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeTable<T>(int uid) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
var list = sqlSugarScope?.Queryable<T>()
|
||||||
|
.Select(x => x)
|
||||||
|
.Select<T>()
|
||||||
|
.Where(x => x.Uid == uid)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var inst in list!.Select(instance => (instance as BaseDatabaseDataHelper)!))
|
||||||
|
{
|
||||||
|
if (!UidInstanceMap.TryGetValue(inst.Uid, out var value))
|
||||||
|
{
|
||||||
|
value = [];
|
||||||
|
UidInstanceMap[inst.Uid] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Add(inst); // add to the map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeSqlite()
|
||||||
|
{
|
||||||
|
var baseType = typeof(BaseDatabaseDataHelper);
|
||||||
|
var assembly = typeof(BaseDatabaseDataHelper).Assembly;
|
||||||
|
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
|
||||||
|
foreach (var type in types)
|
||||||
|
typeof(DatabaseHelper).GetMethod("InitializeSqliteTable")?.MakeGenericMethod(type).Invoke(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO NOT DEL ReSharper disable once UnusedMember.Global
|
||||||
|
public static void InitializeSqliteTable<T>() where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sqlSugarScope?.CodeFirst.InitTables<T>();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? GetInstance<T>(int uid, bool forceReload = false) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!forceReload && UidInstanceMap.TryGetValue(uid, out var value))
|
||||||
|
{
|
||||||
|
var instance = value.OfType<T>().FirstOrDefault();
|
||||||
|
if (instance != null) return instance;
|
||||||
|
}
|
||||||
|
var t = sqlSugarScope?.Queryable<T>()
|
||||||
|
.Where(x => x.Uid == uid)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (t is { Count: > 0 })
|
||||||
|
{
|
||||||
|
var instance = t[0];
|
||||||
|
|
||||||
|
if (!UidInstanceMap.TryGetValue(uid, out var list))
|
||||||
|
{
|
||||||
|
list = new List<BaseDatabaseDataHelper>();
|
||||||
|
UidInstanceMap[uid] = list;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.RemoveAll(i => i is T);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Unsupported type", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetInstanceOrCreateNew<T>(int uid) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
var instance = GetInstance<T>(uid);
|
||||||
|
if (instance != null) return instance;
|
||||||
|
|
||||||
|
instance = new T
|
||||||
|
{
|
||||||
|
Uid = uid
|
||||||
|
};
|
||||||
|
CreateInstance(instance);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<T>? GetAllInstance<T>() where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return sqlSugarScope?.Queryable<T>()
|
||||||
|
.Select(x => x)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Unsupported type", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateInstance<T>(T instance) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
sqlSugarScope?.Updateable(instance).ExecuteCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateInstance<T>(T instance) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
sqlSugarScope?.Insertable(instance).ExecuteCommand();
|
||||||
|
if (!UidInstanceMap.TryGetValue(instance.Uid, out var value))
|
||||||
|
{
|
||||||
|
value = [];
|
||||||
|
UidInstanceMap[instance.Uid] = value;
|
||||||
|
}
|
||||||
|
value.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteInstance<T>(int key) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sqlSugarScope?.Deleteable<T>().Where(x => x.Uid == key).ExecuteCommand();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("An error occurred while delete the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteAllInstance(int key)
|
||||||
|
{
|
||||||
|
|
||||||
|
var value = UidInstanceMap[key];
|
||||||
|
var baseType = typeof(BaseDatabaseDataHelper);
|
||||||
|
var assembly = typeof(BaseDatabaseDataHelper).Assembly;
|
||||||
|
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
var instance = value.Find(x => x.GetType() == type);
|
||||||
|
if (instance != null)
|
||||||
|
typeof(DatabaseHelper).GetMethod("DeleteInstance")?.MakeGenericMethod(type)
|
||||||
|
.Invoke(null, [key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UidInstanceMap.TryRemove(key, out var instances))
|
||||||
|
ToSaveUidList.RemoveAll(x => x == key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto save per 5 min
|
||||||
|
public static void CalcSaveDatabase()
|
||||||
|
{
|
||||||
|
if (LastSaveTick + TimeSpan.TicksPerMinute * 5 > DateTime.UtcNow.Ticks) return;
|
||||||
|
SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveDatabase()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var prev = DateTime.Now;
|
||||||
|
var list = ToSaveUidList.ToList(); // copy the list to avoid the exception
|
||||||
|
foreach (var uid in list)
|
||||||
|
{
|
||||||
|
var value = UidInstanceMap[uid];
|
||||||
|
var baseType = typeof(BaseDatabaseDataHelper);
|
||||||
|
var assembly = typeof(BaseDatabaseDataHelper).Assembly;
|
||||||
|
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
var instance = value.Find(x => x.GetType() == type);
|
||||||
|
if (instance != null)
|
||||||
|
typeof(DatabaseHelper).GetMethod("SaveDatabaseType")?.MakeGenericMethod(type)
|
||||||
|
.Invoke(null, [instance]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var t = (DateTime.Now - prev).TotalSeconds;
|
||||||
|
logger.Info(I18NManager.Translate("Server.ServerInfo.SaveDatabase",
|
||||||
|
Math.Round(t, 2).ToString(CultureInfo.InvariantCulture)));
|
||||||
|
|
||||||
|
ToSaveUidList.Clear();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("An error occurred while saving the database", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
LastSaveTick = DateTime.UtcNow.Ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO NOT DEL ReSharper save database from cache
|
||||||
|
public static void SaveDatabaseType<T>(T instance) where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sqlSugarScope?.Updateable(instance).ExecuteCommand();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("An error occurred while saving the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
Common/Database/Inventory/InventoryData.cs
Normal file
75
Common/Database/Inventory/InventoryData.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using KianaBH.Proto;
|
||||||
|
using SqlSugar;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Inventory;
|
||||||
|
|
||||||
|
[SugarTable("InventoryData")]
|
||||||
|
public class InventoryData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsJson = true)] public List<ItemData> MaterialItems { get; set; } = [];
|
||||||
|
|
||||||
|
[SugarColumn(IsJson = true)] public List<ItemData> WeaponItems { get; set; } = [];
|
||||||
|
|
||||||
|
[SugarColumn(IsJson = true)] public List<ItemData> StigmataItems { get; set; } = [];
|
||||||
|
|
||||||
|
public int NextUniqueId { get; set; } = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ItemData
|
||||||
|
{
|
||||||
|
public int UniqueId { get; set; }
|
||||||
|
public int ItemId { get; set; }
|
||||||
|
public int SubItemId { get; set; }
|
||||||
|
public int Count { get; set; }
|
||||||
|
public int Level { get; set; }
|
||||||
|
public int Exp { get; set; }
|
||||||
|
public bool Locked { get; set; }
|
||||||
|
public bool AffixIdentify { get; set; }
|
||||||
|
public uint CancelLockedTime { get; set; }
|
||||||
|
public bool Extracted { get; set; }
|
||||||
|
public int SlotNum { get; set; }
|
||||||
|
public int Refine { get; set; }
|
||||||
|
public int Promote { get; set; }
|
||||||
|
public int Homology { get; set; }
|
||||||
|
public List<int> QuantumBranchLists { get; set; } = [];
|
||||||
|
public List<Rune> RuneLists { get; set; } = [];
|
||||||
|
public List<Rune> WaitSelectRuneLists { get; set; } = [];
|
||||||
|
public List<RuneGroup> WaitSelectRuneGroupLists { get; set; } = [];
|
||||||
|
public int EquipAvatar { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public Material ToMaterialProto()
|
||||||
|
{
|
||||||
|
return new Material
|
||||||
|
{
|
||||||
|
Id = (uint)ItemId,
|
||||||
|
Num = (uint)Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Weapon ToWeaponProto()
|
||||||
|
{
|
||||||
|
return new Weapon
|
||||||
|
{
|
||||||
|
Id = (uint)ItemId,
|
||||||
|
UniqueId = (uint)UniqueId,
|
||||||
|
Level = (uint)Level,
|
||||||
|
Exp = (uint)Exp,
|
||||||
|
IsProtected = Locked,
|
||||||
|
IsExtracted = Extracted,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RuneGroup
|
||||||
|
{
|
||||||
|
public int UniqueId { get; set; }
|
||||||
|
public List<Rune> RuneLists { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Rune
|
||||||
|
{
|
||||||
|
public int RuneId { get; set; }
|
||||||
|
public int Strength { get; set; }
|
||||||
|
}
|
||||||
19
Common/Database/Lineup/LineupData.cs
Normal file
19
Common/Database/Lineup/LineupData.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Lineup;
|
||||||
|
|
||||||
|
[SugarTable("Lineup")]
|
||||||
|
public class LineupData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsJson = true)] public Dictionary<int, LineupInfo> Lineups { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LineupInfo
|
||||||
|
{
|
||||||
|
public uint Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public uint AstraMateId { get; set; }
|
||||||
|
public bool IsUsingAstraMate { get; set; }
|
||||||
|
public List<uint> AvatarIds { get; set; } = [];
|
||||||
|
public List<uint> ElfIds { get; set; } = [];
|
||||||
|
}
|
||||||
9
Common/Database/Player/GuideData.cs
Normal file
9
Common/Database/Player/GuideData.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Player;
|
||||||
|
|
||||||
|
[SugarTable("player_guide")]
|
||||||
|
public class GuideData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsJson = true)] public List<uint> GuideFinishList { get; set; } = [];
|
||||||
|
}
|
||||||
73
Common/Database/Player/PlayerData.cs
Normal file
73
Common/Database/Player/PlayerData.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using KianaBH.Proto;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace KianaBH.Database.Player;
|
||||||
|
|
||||||
|
[SugarTable("Player")]
|
||||||
|
public class PlayerData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
public string? Name { get; set; } = "";
|
||||||
|
public string? Signature { get; set; } = "KianaPS";
|
||||||
|
public uint Level { get; set; } = 88;
|
||||||
|
public uint Exp { get; set; } = 0;
|
||||||
|
public uint HCoin { get; set; } = 0;
|
||||||
|
public uint Stamina { get; set; } = 240;
|
||||||
|
public uint HeadIcon { get; set; } = 161090;
|
||||||
|
public uint HeadFrame { get; set; } = 200001;
|
||||||
|
public uint WarshipId { get; set; } = 400004;
|
||||||
|
public uint PhonePendantId { get; set; } = 350005;
|
||||||
|
public uint AssistantAvatarId { get; set; } = 101;
|
||||||
|
public uint BirthDay { get; set; } = 0;
|
||||||
|
[SugarColumn(IsJson = true)] public WarshipAvatarData WarshipAvatar { get; set; } = new();
|
||||||
|
[SugarColumn(IsNullable = true)] public long LastActiveTime { get; set; }
|
||||||
|
public long RegisterTime { get; set; } = Extensions.GetUnixSec();
|
||||||
|
|
||||||
|
public static PlayerData? GetPlayerByUid(long uid)
|
||||||
|
{
|
||||||
|
var result = DatabaseHelper.GetInstance<PlayerData>((int)uid);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public GetMainDataRsp ToProto()
|
||||||
|
{
|
||||||
|
return new GetMainDataRsp
|
||||||
|
{
|
||||||
|
IsAll = true,
|
||||||
|
AssistantAvatarId = 0,
|
||||||
|
Birthday = BirthDay,
|
||||||
|
Nickname = Name,
|
||||||
|
Level = Level,
|
||||||
|
Exp = Exp,
|
||||||
|
Hcoin = HCoin,
|
||||||
|
CustomHeadId = HeadIcon,
|
||||||
|
RegisterTime = (uint)RegisterTime,
|
||||||
|
WarshipAvatar = new Proto.WarshipAvatarData
|
||||||
|
{
|
||||||
|
WarshipFirstAvatarId = 0,
|
||||||
|
WarshipSecondAvatarId = 0,
|
||||||
|
},
|
||||||
|
SelfDesc = Signature,
|
||||||
|
UseFrameId = HeadFrame,
|
||||||
|
OnPhonePendantId = PhonePendantId,
|
||||||
|
Stamina = Stamina,
|
||||||
|
StaminaRecoverConfigTime = GameConstants.STAMINA_RECOVERY_TIME,
|
||||||
|
StaminaRecoverLeftTime = GameConstants.STAMINA_RECOVERY_TIME,
|
||||||
|
EquipmentSizeLimit = GameConstants.INVENTORY_MAX_EQUIPMENT,
|
||||||
|
TypeList = { Enumerable.Range(2, 38).Select(i => (uint)i) },
|
||||||
|
LevelLockId = 1,
|
||||||
|
WarshipTheme = new WarshipThemeData
|
||||||
|
{
|
||||||
|
WarshipId=0
|
||||||
|
},
|
||||||
|
TotalLoginDays = 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WarshipAvatarData
|
||||||
|
{
|
||||||
|
public uint FirstAvatarId { get; set; } = 101;
|
||||||
|
public uint SecondAvatarId { get; set; } = 0;
|
||||||
|
}
|
||||||
8
Common/Enums/Item/ItemMainTypeEnum.cs
Normal file
8
Common/Enums/Item/ItemMainTypeEnum.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace KianaBH.Enums.Item;
|
||||||
|
|
||||||
|
public enum ItemMainTypeEnum
|
||||||
|
{
|
||||||
|
Material = 1,
|
||||||
|
Weapon = 2,
|
||||||
|
Stigmata = 3,
|
||||||
|
}
|
||||||
9
Common/Enums/Language/ProgramLanguageTypeEnum.cs
Normal file
9
Common/Enums/Language/ProgramLanguageTypeEnum.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace KianaBH.Enums.Language;
|
||||||
|
|
||||||
|
public enum ProgramLanguageTypeEnum
|
||||||
|
{
|
||||||
|
EN = 0,
|
||||||
|
CHS = 1,
|
||||||
|
CHT = 2,
|
||||||
|
JP = 3
|
||||||
|
}
|
||||||
13
Common/Enums/Player/BanTypeEnum.cs
Normal file
13
Common/Enums/Player/BanTypeEnum.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
public enum BanTypeEnum
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
UseThirdPartySoftware = 1,
|
||||||
|
ThirdPartySoftware = 2,
|
||||||
|
AbnormalLogin = 4,
|
||||||
|
AbnormalAccount = 5,
|
||||||
|
ViolationTermsService = 6,
|
||||||
|
AccountRisk = 7,
|
||||||
|
Unknown = 8
|
||||||
|
}
|
||||||
7
Common/Enums/Player/FriendEnum.cs
Normal file
7
Common/Enums/Player/FriendEnum.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
public enum ServerEnum
|
||||||
|
{
|
||||||
|
Console = 0,
|
||||||
|
Chat = 1
|
||||||
|
}
|
||||||
9
Common/Enums/Player/OperationEnum.cs
Normal file
9
Common/Enums/Player/OperationEnum.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
public enum OperationEnum
|
||||||
|
{
|
||||||
|
And = 0,
|
||||||
|
Or = 1,
|
||||||
|
Not = 2,
|
||||||
|
Unknow = 3
|
||||||
|
}
|
||||||
9
Common/Enums/Player/PermEnum.cs
Normal file
9
Common/Enums/Player/PermEnum.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
public enum PermEnum
|
||||||
|
{
|
||||||
|
Trial = 0,
|
||||||
|
Support = 1,
|
||||||
|
Admin = 2,
|
||||||
|
Other = 10
|
||||||
|
}
|
||||||
13
Common/Enums/Player/PlayerStatusEnum.cs
Normal file
13
Common/Enums/Player/PlayerStatusEnum.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
public enum PlayerStatusEnum
|
||||||
|
{
|
||||||
|
Offline = 0,
|
||||||
|
Explore = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PlayerSubStatusEnum
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Battle = 1
|
||||||
|
}
|
||||||
29
Common/Enums/Player/RegionEnum.cs
Normal file
29
Common/Enums/Player/RegionEnum.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
namespace KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
public enum BaseRegionEnum
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
CN = 11,
|
||||||
|
OS = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RegionEnum
|
||||||
|
{
|
||||||
|
PRODCN = 0,
|
||||||
|
SANDBOXCN = 1,
|
||||||
|
PRODOVERSEA = 2,
|
||||||
|
SANDBOXOVERSEA = 3,
|
||||||
|
PRODCNPRE = 4,
|
||||||
|
PRODOVERSEAPRE = 5,
|
||||||
|
TESTCN = 6,
|
||||||
|
TESTOVERSEA = 7,
|
||||||
|
PETCN = 8,
|
||||||
|
BETACN = 9,
|
||||||
|
BETACNPRE = 10,
|
||||||
|
BETAOVERSEA = 11,
|
||||||
|
PETOS = 12,
|
||||||
|
HOTFIXCN = 19,
|
||||||
|
HOTFIXOVERSEA = 20,
|
||||||
|
UNKNOWN = 21,
|
||||||
|
UNKNOWN2 = 22,
|
||||||
|
}
|
||||||
102
Common/Internationalization/I18nManager.cs
Normal file
102
Common/Internationalization/I18nManager.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using KianaBH.Enums.Language;
|
||||||
|
using KianaBH.Internationalization.Message;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace KianaBH.Internationalization;
|
||||||
|
|
||||||
|
public static class I18NManager
|
||||||
|
{
|
||||||
|
public static Logger Logger = new("I18nManager");
|
||||||
|
|
||||||
|
public static object Language { get; set; } = new LanguageEN();
|
||||||
|
public static Dictionary<string, Dictionary<ProgramLanguageTypeEnum, object>> PluginLanguages { get; } = [];
|
||||||
|
|
||||||
|
public static void LoadLanguage()
|
||||||
|
{
|
||||||
|
var languageStr = "KianaBH.Internationalization.Message.Language" +
|
||||||
|
ConfigManager.Config.ServerOption.Language;
|
||||||
|
var languageType = Type.GetType(languageStr);
|
||||||
|
if (languageType == null)
|
||||||
|
{
|
||||||
|
Logger.Warn("Language not found, fallback to EN");
|
||||||
|
// fallback to English
|
||||||
|
languageType = Type.GetType("KianaBH.Internationalization.Message.LanguageEN")!;
|
||||||
|
}
|
||||||
|
|
||||||
|
var language = Activator.CreateInstance(languageType) ?? throw new Exception("Language not found");
|
||||||
|
Language = language;
|
||||||
|
|
||||||
|
Logger.Info(Translate("Server.ServerInfo.LoadedItem", Translate("Word.Language")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadPluginLanguage(Dictionary<string, List<Type>> pluginAssemblies)
|
||||||
|
{
|
||||||
|
foreach (var (pluginName, types) in pluginAssemblies)
|
||||||
|
{
|
||||||
|
var languageType = types.FindAll(x => x.GetCustomAttribute<PluginLanguageAttribute>() != null);
|
||||||
|
if (languageType.Count == 0) // no language to use
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PluginLanguages.Add(pluginName, []);
|
||||||
|
foreach (var type in languageType)
|
||||||
|
{
|
||||||
|
var attr = type.GetCustomAttribute<PluginLanguageAttribute>();
|
||||||
|
if (attr == null) continue;
|
||||||
|
|
||||||
|
var language = Activator.CreateInstance(type);
|
||||||
|
if (language == null) continue;
|
||||||
|
PluginLanguages[pluginName].Add(attr.LanguageType, language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Translate(string key, params string[] args)
|
||||||
|
{
|
||||||
|
var pluginLangs = PluginLanguages.Values;
|
||||||
|
var langs = (from pluginLang in pluginLangs
|
||||||
|
from o in pluginLang
|
||||||
|
where o.Key == Enum.Parse<ProgramLanguageTypeEnum>(ConfigManager.Config.ServerOption.Language)
|
||||||
|
select o.Value).ToList(); // get all plugin languages
|
||||||
|
langs.Add(Language); // add server language
|
||||||
|
|
||||||
|
var result = langs.Select(lang => GetNestedPropertyValue(lang, key)).OfType<string>().FirstOrDefault() ?? key;
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
return args.Aggregate(result, (current, arg) => current.Replace("{" + index++ + "}", arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TranslateAsCertainLang(string langStr, string key, params string[] args)
|
||||||
|
{
|
||||||
|
var languageStr = "KianaBH.Internationalization.Message.Language" +
|
||||||
|
langStr;
|
||||||
|
var languageType = Type.GetType(languageStr) ??
|
||||||
|
Type.GetType("KianaBH.Internationalization.Message.LanguageEN")!;
|
||||||
|
var language = Activator.CreateInstance(languageType) ?? throw new Exception("Language not found");
|
||||||
|
|
||||||
|
List<object> langs = [language];
|
||||||
|
|
||||||
|
var result = langs.Select(lang => GetNestedPropertyValue(lang, key)).OfType<string>().FirstOrDefault() ?? key;
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
return args.Aggregate(result, (current, arg) => current.Replace("{" + index++ + "}", arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetNestedPropertyValue(object? obj, string propertyName)
|
||||||
|
{
|
||||||
|
foreach (var part in propertyName.Split('.'))
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
var type = obj.GetType();
|
||||||
|
var property = type.GetProperty(part);
|
||||||
|
if (property == null) return null;
|
||||||
|
|
||||||
|
obj = property.GetValue(obj, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string?)obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
527
Common/Internationalization/Message/LanguageCHS.cs
Normal file
527
Common/Internationalization/Message/LanguageCHS.cs
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
namespace KianaBH.Internationalization.Message;
|
||||||
|
|
||||||
|
#region Root
|
||||||
|
|
||||||
|
public class LanguageCHS
|
||||||
|
{
|
||||||
|
public GameTextCHS Game { get; } = new();
|
||||||
|
public ServerTextCHS Server { get; } = new();
|
||||||
|
public WordTextCHS Word { get; } = new(); // a placeholder for the actual word text
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 1
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game
|
||||||
|
/// </summary>
|
||||||
|
public class GameTextCHS
|
||||||
|
{
|
||||||
|
public CommandTextCHS Command { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server
|
||||||
|
/// </summary>
|
||||||
|
public class ServerTextCHS
|
||||||
|
{
|
||||||
|
public WebTextCHS Web { get; } = new();
|
||||||
|
public ServerInfoTextCHS ServerInfo { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Word
|
||||||
|
/// </summary>
|
||||||
|
public class WordTextCHS
|
||||||
|
{
|
||||||
|
public string Rank => "星魂";
|
||||||
|
public string Avatar => "角色";
|
||||||
|
public string Material => "材料";
|
||||||
|
public string Pet => "宠物";
|
||||||
|
public string Relic => "遗器";
|
||||||
|
public string Equipment => "光锥";
|
||||||
|
public string Talent => "行迹";
|
||||||
|
public string Banner => "卡池";
|
||||||
|
public string Activity => "活动";
|
||||||
|
public string CdKey => "兑换码";
|
||||||
|
public string VideoKey => "过场动画密钥";
|
||||||
|
public string Buff => "祝福";
|
||||||
|
public string Miracle => "奇物";
|
||||||
|
public string Unlock => "奢侈品";
|
||||||
|
public string TrainParty => "派对车厢";
|
||||||
|
|
||||||
|
// server info
|
||||||
|
public string Config => "配置文件";
|
||||||
|
public string Language => "语言";
|
||||||
|
public string Log => "日志";
|
||||||
|
public string GameData => "游戏数据";
|
||||||
|
public string Cache => "资源缓存";
|
||||||
|
public string CustomData => "自定义数据";
|
||||||
|
public string Database => "数据库";
|
||||||
|
public string Command => "命令";
|
||||||
|
public string SSL => "SSL";
|
||||||
|
public string Ec2b => "Ec2b";
|
||||||
|
public string SdkServer => "Web服务器";
|
||||||
|
public string Handler => "包处理器";
|
||||||
|
public string Dispatch => "全局分发";
|
||||||
|
public string Game => "游戏";
|
||||||
|
public string Handbook => "手册";
|
||||||
|
public string NotFound => "未找到";
|
||||||
|
public string Error => "错误";
|
||||||
|
public string FloorInfo => "区域文件";
|
||||||
|
public string FloorGroupInfo => "区域组文件";
|
||||||
|
public string FloorMissingResult => "传送与世界生成";
|
||||||
|
public string FloorGroupMissingResult => "传送、怪物战斗与世界生成";
|
||||||
|
public string Mission => "任务";
|
||||||
|
public string MissionInfo => "任务文件";
|
||||||
|
public string SubMission => "子任务";
|
||||||
|
public string SubMissionInfo => "子任务文件";
|
||||||
|
public string MazeSkill => "角色秘技";
|
||||||
|
public string MazeSkillInfo => "角色秘技文件";
|
||||||
|
public string Dialogue => "模拟宇宙事件";
|
||||||
|
public string DialogueInfo => "模拟宇宙事件文件";
|
||||||
|
public string Performance => "剧情操作";
|
||||||
|
public string PerformanceInfo => "剧情操作文件";
|
||||||
|
public string RogueChestMap => "模拟宇宙地图";
|
||||||
|
public string RogueChestMapInfo => "模拟宇宙地图文件";
|
||||||
|
public string ChessRogueRoom => "模拟宇宙DLC";
|
||||||
|
public string ChessRogueRoomInfo => "模拟宇宙DLC文件";
|
||||||
|
public string SummonUnit => "秘技生成";
|
||||||
|
public string SummonUnitInfo => "秘技生成文件";
|
||||||
|
public string RogueTournRoom => "差分宇宙";
|
||||||
|
public string RogueTournRoomInfo => "差分宇宙房间文件";
|
||||||
|
public string TypesOfRogue => "类型的模拟宇宙";
|
||||||
|
public string RogueMagicRoom => "不可知域";
|
||||||
|
public string RogueMagicRoomInfo => "不可知域房间文件";
|
||||||
|
public string RogueDiceSurface => "骰面效果";
|
||||||
|
public string RogueDiceSurfaceInfo => "骰面效果文件";
|
||||||
|
public string AdventureModifier => "AdventureModifier";
|
||||||
|
public string AdventureModifierInfo => "AdventureModifier文件";
|
||||||
|
public string RogueMapGen => "RogueMapGen文件";
|
||||||
|
public string RogueMiracleGroup => "RogueMiracleGroup文件";
|
||||||
|
public string RogueMiracleEffectGen => "RogueMiracleEffectGen文件";
|
||||||
|
|
||||||
|
public string DatabaseAccount => "数据库账号";
|
||||||
|
public string Tutorial => "教程";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 2
|
||||||
|
|
||||||
|
#region GameText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command
|
||||||
|
/// </summary>
|
||||||
|
public class CommandTextCHS
|
||||||
|
{
|
||||||
|
public NoticeTextCHS Notice { get; } = new();
|
||||||
|
|
||||||
|
public GenderTextCHS Gender { get; } = new();
|
||||||
|
public AvatarTextCHS Avatar { get; } = new();
|
||||||
|
public AnnounceTextCHS Announce { get; } = new();
|
||||||
|
public BanTextCHS Ban { get; } = new();
|
||||||
|
public GiveTextCHS Give { get; } = new();
|
||||||
|
public GiveAllTextCHS GiveAll { get; } = new();
|
||||||
|
public LineupTextCHS Lineup { get; } = new();
|
||||||
|
public HelpTextCHS Help { get; } = new();
|
||||||
|
public KickTextCHS Kick { get; } = new();
|
||||||
|
public MissionTextCHS Mission { get; } = new();
|
||||||
|
public RelicTextCHS Relic { get; } = new();
|
||||||
|
public ReloadTextCHS Reload { get; } = new();
|
||||||
|
public RogueTextCHS Rogue { get; } = new();
|
||||||
|
public SceneTextCHS Scene { get; } = new();
|
||||||
|
public UnlockAllTextCHS UnlockAll { get; } = new();
|
||||||
|
public MailTextCHS Mail { get; } = new();
|
||||||
|
public RaidTextCHS Raid { get; } = new();
|
||||||
|
public AccountTextCHS Account { get; } = new();
|
||||||
|
public UnstuckTextCHS Unstuck { get; } = new();
|
||||||
|
public SetlevelTextCHS Setlevel { get; } = new();
|
||||||
|
public PermissionTextCHS Permission { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ServerText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server.Web
|
||||||
|
/// </summary>
|
||||||
|
public class WebTextCHS
|
||||||
|
{
|
||||||
|
public string Maintain => "服务器正在维修, 请稍后尝试。";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server.ServerInfo
|
||||||
|
/// </summary>
|
||||||
|
public class ServerInfoTextCHS
|
||||||
|
{
|
||||||
|
public string Shutdown => "关闭中…";
|
||||||
|
public string CancelKeyPressed => "已按下取消键 (Ctrl + C), 服务器即将关闭…";
|
||||||
|
public string StartingServer => "正在启动 KianaBH";
|
||||||
|
public string CurrentVersion => "当前服务端支持的版本: {0}";
|
||||||
|
public string InvalidVersion => "当前为不受支持的游戏版本 {0}\n请更新游戏到 {1}";
|
||||||
|
public string LoadingItem => "正在加载 {0}…";
|
||||||
|
public string GeneratingItem => "正在生成 {0}…";
|
||||||
|
public string WaitingItem => "正在等待进程 {0} 完成…";
|
||||||
|
public string RegisterItem => "注册了 {0} 个 {1}。";
|
||||||
|
public string FailedToLoadItem => "加载 {0} 失败。";
|
||||||
|
public string NewClientSecretKey => "客户端密钥不存在, 正在生成新的客户端密钥。";
|
||||||
|
public string FailedToInitializeItem => "初始化 {0} 失败。";
|
||||||
|
public string FailedToReadItem => "读取 {0} 失败, 文件{1}";
|
||||||
|
public string GeneratedItem => "已生成 {0}。";
|
||||||
|
public string LoadedItem => "已加载 {0}。";
|
||||||
|
public string LoadedItems => "已加载 {0} 个 {1}。";
|
||||||
|
public string ServerRunning => "{0} 服务器正在监听 {1}";
|
||||||
|
public string ServerStarted => "启动完成!用时 {0}s, 击败了99%的用户, 输入 ‘help’ 来获取命令帮助"; // 玩梗, 考虑英语版本将其本土化
|
||||||
|
public string MissionEnabled => "任务系统已启用, 此功能仍在开发中, 且可能不会按预期工作, 如果遇见任何bug, 请汇报给开发者。";
|
||||||
|
public string KeyStoreError => "SSL证书不存在, 已关闭SSL功能。";
|
||||||
|
public string CacheLoadSkip => "已跳过缓存加载。";
|
||||||
|
|
||||||
|
public string ConfigMissing => "{0} 缺失, 请检查你的资源文件夹: {1}, {2} 可能不能使用。";
|
||||||
|
public string UnloadedItems => "卸载了所有 {0}。";
|
||||||
|
public string SaveDatabase => "已保存数据库, 用时 {0}s";
|
||||||
|
public string WaitForAllDone => "现在还不可以进入游戏, 请等待所有项目加载完成后再试";
|
||||||
|
|
||||||
|
public string UnhandledException => "发生未经处理的异常: {0}";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 3
|
||||||
|
|
||||||
|
#region CommandText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Notice
|
||||||
|
/// </summary>
|
||||||
|
public class NoticeTextCHS
|
||||||
|
{
|
||||||
|
public string PlayerNotFound => "未找到玩家!";
|
||||||
|
public string InvalidArguments => "无效的参数!";
|
||||||
|
public string NoPermission => "你没有权限这么做!";
|
||||||
|
public string CommandNotFound => "未找到命令! 输入 '/help' 来获取帮助";
|
||||||
|
public string TargetOffline => "目标 {0}({1}) 离线了!清除当前目标";
|
||||||
|
public string TargetFound => "找到目标 {0}({1}), 下一次命令将默认对其执行";
|
||||||
|
public string TargetNotFound => "未找到目标 {0}!";
|
||||||
|
public string InternalError => "在处理命令时发生了内部错误: {0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Gender
|
||||||
|
/// </summary>
|
||||||
|
public class GenderTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "切换主角的性别";
|
||||||
|
public string Usage => "用法: /gender [man/woman]";
|
||||||
|
public string GenderNotSpecified => "性别不存在!";
|
||||||
|
public string GenderChanged => "性别已更改!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.UnlockAll
|
||||||
|
/// </summary>
|
||||||
|
public class UnlockAllTextCHS
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"解锁所有在类别内的对象\n" +
|
||||||
|
"使用 /unlockall mission 以完成所有任务, 使用后会被踢出, 重新登录后可能会被教程卡住, 请谨慎使用\n" +
|
||||||
|
"使用 /unlockall tutorial 以解锁所有教程, 使用后会被踢出, 用于部分界面卡住无法行动的情况\n" +
|
||||||
|
"使用 /unlockall rogue 以解锁所有类型模拟宇宙, 使用后会被踢出, 建议与 /unlockall tutorial 搭配使用以获取更好效果";
|
||||||
|
public string Usage => "用法: /unlockall [mission/tutorial/rogue]";
|
||||||
|
public string UnlockedAll => "已解锁/完成所有{0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Avatar
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "设定玩家已有角色的属性, -1为所有已拥有角色";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /avatar talent [角色ID/-1] [行迹等级]\n" +
|
||||||
|
"用法: /avatar rank [角色ID/-1] [星魂]\n" +
|
||||||
|
"用法: /avatar level [角色ID/-1] [角色等级]";
|
||||||
|
public string InvalidLevel => "{0} 等级无效!";
|
||||||
|
public string AllAvatarsLevelSet => "已将全部角色 {0} 等级设置为 {1}.";
|
||||||
|
public string AvatarLevelSet => "已将 {0} 角色 {1} 等级设置为 {2}.";
|
||||||
|
public string AvatarNotFound => "角色不存在!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Give
|
||||||
|
/// </summary>
|
||||||
|
public class GiveTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "给予玩家物品";
|
||||||
|
public string Usage => "用法: /give [物品ID] l[等级] x[数量] r[叠影]";
|
||||||
|
public string ItemNotFound => "未找到物品!";
|
||||||
|
public string GiveItem => "已给予 {0} {1} 个物品 {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.GiveAll
|
||||||
|
/// </summary>
|
||||||
|
public class GiveAllTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "给予玩家全部指定类型的物品";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /giveall avatar r[星魂] l[等级]\n" +
|
||||||
|
"用法: /giveall material x[数量]\n" +
|
||||||
|
"用法: /giveall equipment r[叠影] l[等级] x[数量]\n" +
|
||||||
|
"用法: /giveall relic x[数量]\n" +
|
||||||
|
"用法: /giveall unlock\n" +
|
||||||
|
"用法: /giveall train\n" +
|
||||||
|
"用法: /giveall path";
|
||||||
|
public string GiveAllItems => "已给予所有 {0}, 各 {1} 个.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Lineup
|
||||||
|
/// </summary>
|
||||||
|
public class LineupTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家的队伍信息";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /lineup mp\n" +
|
||||||
|
"用法: /lineup sp\n" +
|
||||||
|
"用法: /lineup heal";
|
||||||
|
public string GainedMp => "成功恢复秘技点!";
|
||||||
|
public string GainedSp => "成功恢复能量!";
|
||||||
|
public string HealedAllAvatars => "成功治愈当前队伍中的所有角色!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Help
|
||||||
|
/// </summary>
|
||||||
|
public class HelpTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "显示帮助信息";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /help\n" +
|
||||||
|
"用法: /help [命令]";
|
||||||
|
public string Commands => "命令: ";
|
||||||
|
public string CommandPermission => "所需权限: ";
|
||||||
|
public string CommandAlias => "命令别名: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Kick
|
||||||
|
/// </summary>
|
||||||
|
public class KickTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "踢出玩家";
|
||||||
|
public string Usage => "用法: /kick";
|
||||||
|
public string PlayerKicked => "玩家 {0} 已被踢出!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Mission
|
||||||
|
/// </summary>
|
||||||
|
public class MissionTextCHS
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"管理玩家的任务\n" +
|
||||||
|
"使用 pass 完成当前正在进行的所有任务, 此命令易造成严重卡顿, 请尽量使用 /mission finish 替代\n" +
|
||||||
|
"使用 finish [子任务ID] 完成指定子任务, 请浏览 handbook 来获取子任务ID\n" +
|
||||||
|
"使用 finishmain [主任务ID] 完成指定主任务, 请浏览 handbook 来获取主任务ID\n" +
|
||||||
|
"使用 running [-all] 获取正在追踪的任务, 增加'-all'则显示所有正在进行的任务以及可能卡住的任务, 使用后可能会出现较长任务列表, 请注意甄别\n" +
|
||||||
|
"使用 reaccept [主任务ID] 可重新进行指定主任务, 请浏览 handbook 来获取主任务ID";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /mission pass\n" +
|
||||||
|
"用法: /mission finish [子任务ID]\n" +
|
||||||
|
"用法: /mission running [-all]\n" +
|
||||||
|
"用法: /mission reaccept [主任务ID]\n" +
|
||||||
|
"用法: /mission finishmain [主任务ID]";
|
||||||
|
public string AllMissionsFinished => "所有任务已完成!";
|
||||||
|
public string AllRunningMissionsFinished => "共 {0} 个进行中的任务已完成!";
|
||||||
|
public string MissionFinished => "任务 {0} 已完成!";
|
||||||
|
public string InvalidMissionId => "无效的任务ID!";
|
||||||
|
public string NoRunningMissions => "没有正在进行的任务!";
|
||||||
|
public string RunningMissions => "正在进行的任务: ";
|
||||||
|
public string PossibleStuckMissions => "可能卡住的任务: ";
|
||||||
|
public string MainMission => "主任务";
|
||||||
|
public string MissionReAccepted => "重新接受任务 {0}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Relic
|
||||||
|
/// </summary>
|
||||||
|
public class RelicTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家的遗器, 等级限制: 1 ≤ 等级 ≤ 9999";
|
||||||
|
public string Usage => "用法: /relic [遗器ID] [主词条ID] [ID1:等级] [ID2:等级] l[等级] x[数量]";
|
||||||
|
public string RelicNotFound => "遗器不存在!";
|
||||||
|
public string InvalidMainAffixId => "主词条ID无效!";
|
||||||
|
public string InvalidSubAffixId => "副词条ID无效!";
|
||||||
|
public string RelicGiven => "给予玩家 {0} {1} 个遗器 {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Reload
|
||||||
|
/// </summary>
|
||||||
|
public class ReloadTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "重新加载指定的配置";
|
||||||
|
public string Usage => "用法: /reload [banner/activity]";
|
||||||
|
public string ConfigReloaded => "配置 {0} 已重新加载!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Rogue
|
||||||
|
/// </summary>
|
||||||
|
public class RogueTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "管理模拟宇宙数据, -1意为所有已拥有祝福, buff获取祝福, enhance强化祝福";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /rogue money [宇宙碎片数量]\n" +
|
||||||
|
"用法: /rogue buff [祝福ID/-1]\n" +
|
||||||
|
"用法: /rogue miracle [奇物ID]\n" +
|
||||||
|
"用法: /rogue enhance [祝福ID/-1]\n" +
|
||||||
|
"用法: /rogue unstuck - 脱离事件";
|
||||||
|
public string PlayerGainedMoney => "已获得 {0} 宇宙碎片.";
|
||||||
|
public string PlayerGainedAllItems => "已获得所有{0}.";
|
||||||
|
public string PlayerGainedItem => "已获得{0} {1}.";
|
||||||
|
public string PlayerEnhancedBuff => "已强化祝福 {0}.";
|
||||||
|
public string PlayerEnhancedAllBuffs => "已强化所有祝福.";
|
||||||
|
public string PlayerUnstuck => "已脱离事件.";
|
||||||
|
public string NotFoundItem => "未找到 {0}!";
|
||||||
|
public string PlayerNotInRogue => "玩家不在模拟宇宙中!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Scene
|
||||||
|
/// </summary>
|
||||||
|
public class SceneTextCHS
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"管理玩家场景\n" +
|
||||||
|
"使用 PlaneId 默认进入指定场景\n" +
|
||||||
|
"使用 group 来获取组, 使用 prop 来设置道具状态, 在 PropStateEnum 获取状态列表\n" +
|
||||||
|
"使用 unlockall 来解锁场景内所有道具(open状态), 可能导致游戏加载卡条, 使用 /scene reset 解决\n" +
|
||||||
|
"使用 reload 来重新加载当前场景, 并回到初始位置\n" +
|
||||||
|
"使用 reset 来重置指定场景所有道具状态";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /scene [PlaneId]\n" +
|
||||||
|
"用法: /scene cur\n" +
|
||||||
|
"用法: /scene reload\n" +
|
||||||
|
"用法: /scene group\n" +
|
||||||
|
"用法: /scene unlockall\n" +
|
||||||
|
"用法: /scene reset [PlaneId]" +
|
||||||
|
"用法: /scene prop [组ID] [道具ID] [状态]\n" +
|
||||||
|
"用法: /scene remove [实体ID]\n";
|
||||||
|
|
||||||
|
public string LoadedGroups => "已加载组: {0}.";
|
||||||
|
public string PropStateChanged => "道具: {0} 的状态已设置为 {1}.";
|
||||||
|
public string PropNotFound => "未找到道具!";
|
||||||
|
public string EntityRemoved => "实体 {0} 已被移除.";
|
||||||
|
public string EntityNotFound => "未找到实体!";
|
||||||
|
public string AllPropsUnlocked => "所有道具已解锁!";
|
||||||
|
public string SceneChanged => "已进入场景 {0}.";
|
||||||
|
public string SceneReloaded => "场景已重新加载!";
|
||||||
|
public string SceneReset => "已重置场景 {0} 中所有道具状态!";
|
||||||
|
public string CurrentScene => "当前场景 EntryId: {0}, PlaneId: {1}, FloorId: {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Mail
|
||||||
|
/// </summary>
|
||||||
|
public class MailTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "发送邮件";
|
||||||
|
public string Usage => "用法: /mail [发送名称] [标题] [内容] [ID1:数量,ID2:数量]";
|
||||||
|
public string MailSent => "邮件已发送!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Raid
|
||||||
|
/// </summary>
|
||||||
|
public class RaidTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家的任务临时场景";
|
||||||
|
public string Usage => "用法: /raid leave";
|
||||||
|
public string Leaved => "已离开临时场景!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Account
|
||||||
|
/// </summary>
|
||||||
|
public class AccountTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "管理数据库账号";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /account create [用户名] [UID] [密码]\n" +
|
||||||
|
"用法: /account delete [UID]";
|
||||||
|
public string InvalidUid => "UID无效!";
|
||||||
|
public string InvalidAccount => "账号 {0} 无效!";
|
||||||
|
public string CreateSuccess => "账号 {0} 创建成功!";
|
||||||
|
public string DeleteSuccess => "账号 {0} 删除成功!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Announce
|
||||||
|
/// </summary>
|
||||||
|
public class AnnounceTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "发送弹窗公告";
|
||||||
|
public string Usage => "用法: /announce [Text] [Color]";
|
||||||
|
public string SendSuccess => "发送成功!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Ban
|
||||||
|
/// </summary>
|
||||||
|
public class BanTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "封禁或解封用户";
|
||||||
|
public string Usage => "用法: /ban [add/delete]";
|
||||||
|
public string BanSuccess => "账号已封禁!";
|
||||||
|
public string UnBanSuccess => "账号已解封!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Unstuck
|
||||||
|
/// </summary>
|
||||||
|
public class UnstuckTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "将玩家传送回默认场景";
|
||||||
|
public string Usage => "用法: /unstuck [UID]";
|
||||||
|
public string UnstuckSuccess => "已成功将该玩家传送回默认场景.";
|
||||||
|
public string UidNotExist => "该UID不存在!";
|
||||||
|
public string PlayerIsOnline => "该玩家目前在线上!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Setlevel
|
||||||
|
/// </summary>
|
||||||
|
public class SetlevelTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "设定玩家等级";
|
||||||
|
public string Usage => "用法: /setlevel [等级]";
|
||||||
|
public string SetlevelSuccess => "等级设定成功!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Permission
|
||||||
|
/// </summary>
|
||||||
|
public class PermissionTextCHS
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家权限";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /permission add [权限]\n" +
|
||||||
|
"用法: /permission remove [权限]\n" +
|
||||||
|
"用法: /permission clean [权限]";
|
||||||
|
public string InvalidPerm => "权限 {0} 不存在!";
|
||||||
|
public string Added => "已添加权限 {0} 到玩家 {1}!";
|
||||||
|
public string Removed => "已移除玩家 {0} 的权限 {1}!";
|
||||||
|
public string Cleaned => "已清除玩家 {0} 的所有权限!";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
530
Common/Internationalization/Message/LanguageCHT.cs
Normal file
530
Common/Internationalization/Message/LanguageCHT.cs
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
namespace KianaBH.Internationalization.Message;
|
||||||
|
|
||||||
|
#region Root
|
||||||
|
|
||||||
|
public class LanguageCHT
|
||||||
|
{
|
||||||
|
public GameTextCHT Game { get; } = new();
|
||||||
|
public ServerTextCHT Server { get; } = new();
|
||||||
|
public WordTextCHT Word { get; } = new(); // a placeholder for the actual word text
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 1
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game
|
||||||
|
/// </summary>
|
||||||
|
public class GameTextCHT
|
||||||
|
{
|
||||||
|
public CommandTextCHT Command { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server
|
||||||
|
/// </summary>
|
||||||
|
public class ServerTextCHT
|
||||||
|
{
|
||||||
|
public WebTextCHT Web { get; } = new();
|
||||||
|
public ServerInfoTextCHT ServerInfo { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Word
|
||||||
|
/// </summary>
|
||||||
|
public class WordTextCHT
|
||||||
|
{
|
||||||
|
public string Rank => "星魂";
|
||||||
|
public string Avatar => "角色";
|
||||||
|
public string Material => "材料";
|
||||||
|
public string Pet => "寵物";
|
||||||
|
public string Relic => "遺器";
|
||||||
|
public string Equipment => "光錐";
|
||||||
|
public string Talent => "行跡";
|
||||||
|
public string Banner => "卡池";
|
||||||
|
public string Activity => "活動";
|
||||||
|
public string CdKey => "兌換碼";
|
||||||
|
public string VideoKey => "過場動畫金鑰";
|
||||||
|
public string Buff => "祝福";
|
||||||
|
public string Miracle => "奇物";
|
||||||
|
public string Unlock => "奢侈品";
|
||||||
|
public string TrainParty => "派對車廂";
|
||||||
|
|
||||||
|
// server info
|
||||||
|
public string Config => "配置文件";
|
||||||
|
public string Language => "語言";
|
||||||
|
public string Log => "日誌";
|
||||||
|
public string GameData => "遊戲數據";
|
||||||
|
public string Cache => "資源緩存";
|
||||||
|
public string CustomData => "自定義數據";
|
||||||
|
public string Database => "數據庫";
|
||||||
|
public string Command => "命令";
|
||||||
|
public string SSL => "SSL";
|
||||||
|
public string Ec2b => "Ec2b";
|
||||||
|
public string SdkServer => "Web服務器";
|
||||||
|
public string Handler => "包處理器";
|
||||||
|
public string Dispatch => "全局分發";
|
||||||
|
public string Game => "遊戲";
|
||||||
|
public string Handbook => "手冊";
|
||||||
|
public string NotFound => "未找到";
|
||||||
|
public string Error => "錯誤";
|
||||||
|
public string FloorInfo => "區域文件";
|
||||||
|
public string FloorGroupInfo => "區域組文件";
|
||||||
|
public string FloorMissingResult => "傳送與世界生成";
|
||||||
|
public string FloorGroupMissingResult => "傳送、怪物戰鬥與世界生成";
|
||||||
|
public string Mission => "任務";
|
||||||
|
public string MissionInfo => "任務文件";
|
||||||
|
public string SubMission => "子任務";
|
||||||
|
public string SubMissionInfo => "子任務文件";
|
||||||
|
public string MazeSkill => "角色秘技";
|
||||||
|
public string MazeSkillInfo => "角色秘技文件";
|
||||||
|
public string Dialogue => "模擬宇宙事件";
|
||||||
|
public string DialogueInfo => "模擬宇宙事件文件";
|
||||||
|
public string Performance => "劇情操作";
|
||||||
|
public string PerformanceInfo => "劇情操作文件";
|
||||||
|
public string RogueChestMap => "模擬宇宙地圖";
|
||||||
|
public string RogueChestMapInfo => "模擬宇宙地圖文件";
|
||||||
|
public string ChessRogueRoom => "模擬宇宙DLC";
|
||||||
|
public string ChessRogueRoomInfo => "模擬宇宙DLC文件";
|
||||||
|
public string SummonUnit => "秘技生成";
|
||||||
|
public string SummonUnitInfo => "秘技生成文件";
|
||||||
|
public string RogueTournRoom => "差分宇宙";
|
||||||
|
public string RogueTournRoomInfo => "差分宇宙房間文件";
|
||||||
|
public string TypesOfRogue => "類型的模擬宇宙";
|
||||||
|
public string RogueMagicRoom => "不可知域";
|
||||||
|
public string RogueMagicRoomInfo => "不可知域房間文件";
|
||||||
|
public string RogueDiceSurface => "骰面效果";
|
||||||
|
public string RogueDiceSurfaceInfo => "骰面效果文件";
|
||||||
|
public string AdventureModifier => "AdventureModifier";
|
||||||
|
public string AdventureModifierInfo => "AdventureModifier文件";
|
||||||
|
public string RogueMapGen => "RogueMapGen文件";
|
||||||
|
public string RogueMiracleGroup => "RogueMiracleGroup文件";
|
||||||
|
public string RogueMiracleEffectGen => "RogueMiracleEffectGen文件";
|
||||||
|
|
||||||
|
public string DatabaseAccount => "數據庫賬號";
|
||||||
|
public string Tutorial => "教程";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 2
|
||||||
|
|
||||||
|
#region GameText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command
|
||||||
|
/// </summary>
|
||||||
|
public class CommandTextCHT
|
||||||
|
{
|
||||||
|
public NoticeTextCHT Notice { get; } = new();
|
||||||
|
|
||||||
|
public GenderTextCHT Gender { get; } = new();
|
||||||
|
public AvatarTextCHT Avatar { get; } = new();
|
||||||
|
public AnnounceTextCHT Announce { get; } = new();
|
||||||
|
public BanTextCHT Ban { get; } = new();
|
||||||
|
public GiveTextCHT Give { get; } = new();
|
||||||
|
public GiveAllTextCHT GiveAll { get; } = new();
|
||||||
|
public LineupTextCHT Lineup { get; } = new();
|
||||||
|
public HelpTextCHT Help { get; } = new();
|
||||||
|
public KickTextCHT Kick { get; } = new();
|
||||||
|
public MissionTextCHT Mission { get; } = new();
|
||||||
|
public RelicTextCHT Relic { get; } = new();
|
||||||
|
public ReloadTextCHT Reload { get; } = new();
|
||||||
|
public RogueTextCHT Rogue { get; } = new();
|
||||||
|
public SceneTextCHT Scene { get; } = new();
|
||||||
|
public UnlockAllTextCHT UnlockAll { get; } = new();
|
||||||
|
public MailTextCHT Mail { get; } = new();
|
||||||
|
public RaidTextCHT Raid { get; } = new();
|
||||||
|
public AccountTextCHT Account { get; } = new();
|
||||||
|
public UnstuckTextCHT Unstuck { get; } = new();
|
||||||
|
public SetlevelTextCHT Setlevel { get; } = new();
|
||||||
|
public PermissionTextCHT Permission { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ServerText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server.Web
|
||||||
|
/// </summary>
|
||||||
|
public class WebTextCHT
|
||||||
|
{
|
||||||
|
public string Maintain => "服務器正在維修, 請稍後嘗試。";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server.ServerInfo
|
||||||
|
/// </summary>
|
||||||
|
public class ServerInfoTextCHT
|
||||||
|
{
|
||||||
|
public string Shutdown => "關閉中…";
|
||||||
|
public string CancelKeyPressed => "已按下取消鍵 (Ctrl + C), 服務器即將關閉…";
|
||||||
|
public string StartingServer => "正在啟動 KianaBH";
|
||||||
|
public string CurrentVersion => "當前服務端支援的版本: {0}";
|
||||||
|
public string InvalidVersion => "目前為不受支援的遊戲版本 {0}\n請更新遊戲到 {1}";
|
||||||
|
public string LoadingItem => "正在加載 {0}…";
|
||||||
|
public string GeneratingItem => "正在生成 {0}…";
|
||||||
|
public string WaitingItem => "正在等待進程 {0} 完成…";
|
||||||
|
public string RegisterItem => "註冊了 {0} 個 {1}。";
|
||||||
|
public string FailedToLoadItem => "加載 {0} 失敗。";
|
||||||
|
public string NewClientSecretKey => "客戶端密鑰不存在, 正在生成新的客戶端密鑰。";
|
||||||
|
public string FailedToInitializeItem => "初始化 {0} 失敗。";
|
||||||
|
public string FailedToReadItem => "讀取 {0} 失敗, 文件{1}";
|
||||||
|
public string GeneratedItem => "已生成 {0}。";
|
||||||
|
public string LoadedItem => "已加載 {0}。";
|
||||||
|
public string LoadedItems => "已加載 {0} 個 {1}。";
|
||||||
|
public string ServerRunning => "{0} 服務器正在監聽 {1}";
|
||||||
|
public string ServerStarted => "啟動完成!用時 {0}s, 擊敗了99%的用戶, 輸入 『help』 來獲取命令幫助"; // 玩梗, 考慮英語版本將其本土化
|
||||||
|
public string MissionEnabled => "任務系統已啟用, 此功能仍在開發中, 且可能不會按預期工作, 如果遇見任何bug, 請匯報給開發者。";
|
||||||
|
public string KeyStoreError => "SSL證書不存在, 已關閉SSL功能。";
|
||||||
|
public string CacheLoadSkip => "已跳過緩存加載。";
|
||||||
|
|
||||||
|
public string ConfigMissing => "{0} 缺失, 請檢查你的資源文件夾: {1}, {2} 可能不能使用。";
|
||||||
|
public string UnloadedItems => "卸載了所有 {0}。";
|
||||||
|
public string SaveDatabase => "已保存數據庫, 用時 {0}s";
|
||||||
|
public string WaitForAllDone => "現在還不可以進入遊戲, 請等待所有項目加載完成後再試";
|
||||||
|
|
||||||
|
public string UnhandledException => "發生未經處理的異常: {0}";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 3
|
||||||
|
|
||||||
|
#region CommandText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Notice
|
||||||
|
/// </summary>
|
||||||
|
public class NoticeTextCHT
|
||||||
|
{
|
||||||
|
public string PlayerNotFound => "未找到玩家!";
|
||||||
|
public string InvalidArguments => "無效的參數!";
|
||||||
|
public string NoPermission => "你沒有權限這麽做!";
|
||||||
|
public string CommandNotFound => "未找到命令! 輸入 '/help' 來獲取幫助";
|
||||||
|
public string TargetOffline => "目標 {0}({1}) 離線了!清除當前目標";
|
||||||
|
public string TargetFound => "找到目標 {0}({1}), 下一次命令將默認對其執行";
|
||||||
|
public string TargetNotFound => "未找到目標 {0}!";
|
||||||
|
public string InternalError => "在處理命令時發生了內部錯誤: {0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Gender
|
||||||
|
/// </summary>
|
||||||
|
public class GenderTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "切換主角的性別";
|
||||||
|
public string Usage => "用法: /gender [man/woman]";
|
||||||
|
public string GenderNotSpecified => "性別不存在!";
|
||||||
|
public string GenderChanged => "性別已更改!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.UnlockAll
|
||||||
|
/// </summary>
|
||||||
|
public class UnlockAllTextCHT
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"解鎖所有在類別內的對象\n" +
|
||||||
|
"使用 /unlockall mission 以完成所有任務, 使用後會被踢出, 重新登錄後可能會被教程卡住, 請謹慎使用\n" +
|
||||||
|
"使用 /unlockall tutorial 以解鎖所有教程, 使用後會被踢出, 用於部分界面卡住無法行動的情況\n" +
|
||||||
|
"使用 /unlockall rogue 以解鎖所有類型模擬宇宙, 使用後會被踢出, 建議與 /unlockall tutorial 搭配使用以獲取更好效果";
|
||||||
|
|
||||||
|
public string Usage => "用法: /unlockall [mission/tutorial/rogue]";
|
||||||
|
public string UnlockedAll => "已解鎖/完成所有{0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Avatar
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "設定玩家已有角色的屬性, -1意為所有已擁有角色";
|
||||||
|
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /avatar talent [角色ID/-1] [行跡等級]\n" +
|
||||||
|
"用法: /avatar rank [角色ID/-1] [星魂]\n" +
|
||||||
|
"用法: /avatar level [角色ID/-1] [角色等級]";
|
||||||
|
public string InvalidLevel => "{0}等級無效!";
|
||||||
|
public string AllAvatarsLevelSet => "已將全部角色 {0}等級設置為 {1}.";
|
||||||
|
public string AvatarLevelSet => "已將 {0} 角色 {1}等級設置為 {2}.";
|
||||||
|
public string AvatarNotFound => "角色不存在!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Give
|
||||||
|
/// </summary>
|
||||||
|
public class GiveTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "給予玩家物品";
|
||||||
|
public string Usage => "用法: /give [物品ID] l[等級] x[數量] r[疊影]";
|
||||||
|
public string ItemNotFound => "未找到物品!";
|
||||||
|
public string GiveItem => "給予 @{0} {1} 個物品 {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.GiveAll
|
||||||
|
/// </summary>
|
||||||
|
public class GiveAllTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "給予玩家全部指定類型的物品";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /giveall avatar r[星魂] l[等級]\n" +
|
||||||
|
"用法: /giveall material x[數量]\n" +
|
||||||
|
"用法: /giveall equipment r[叠影] l[等級] x[數量]\n" +
|
||||||
|
"用法: /giveall relic l<等級> x<數量>\n" +
|
||||||
|
"用法: /giveall unlock\n" +
|
||||||
|
"用法: /giveall train\n" +
|
||||||
|
"用法: /giveall path";
|
||||||
|
public string GiveAllItems => "已給予所有 {0}, 各 {1} 個.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Lineup
|
||||||
|
/// </summary>
|
||||||
|
public class LineupTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家的隊伍資訊";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /lineup mp\n" +
|
||||||
|
"用法: /lineup sp\n" +
|
||||||
|
"用法: /lineup heal";
|
||||||
|
public string GainedMp => "成功恢復秘技點!";
|
||||||
|
public string GainedSp => "成功恢復能量!";
|
||||||
|
public string HealedAllAvatars => "成功治愈當前隊伍中的所有角色!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Help
|
||||||
|
/// </summary>
|
||||||
|
public class HelpTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "顯示幫助信息";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /help\n" +
|
||||||
|
"用法: /help [命令]";
|
||||||
|
public string Commands => "命令: ";
|
||||||
|
public string CommandPermission => "所需權限: ";
|
||||||
|
public string CommandAlias => "命令別名: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Kick
|
||||||
|
/// </summary>
|
||||||
|
public class KickTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "踢出玩家";
|
||||||
|
public string Usage => "用法: /kick";
|
||||||
|
public string PlayerKicked => "玩家 {0} 已被踢出!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Mission
|
||||||
|
/// </summary>
|
||||||
|
public class MissionTextCHT
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"管理玩家的任務\n" +
|
||||||
|
"使用 pass 完成當前正在進行的所有任務, 此命令易造成嚴重卡頓, 請盡量使用 /mission finish 替代\n" +
|
||||||
|
"使用 finish [子任務ID] 完成指定子任務, 請流覽 handbook 來獲取子任務ID\n" +
|
||||||
|
"使用 finishmain [主任務ID] 完成指定主任務, 請流覽 handbook 來獲取主任務ID\n" +
|
||||||
|
"使用 running [-all] 獲取正在追蹤的任務, 增加'-all'則顯示所有正在進行的任務以及可能卡住的任務, 使用後可能會出現較長任務清單, 請注意甄別\n" +
|
||||||
|
"使用 reaccept [主任務ID] 可重新進行指定主任務, 請流覽 handbook 來獲取主任務ID";
|
||||||
|
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /mission pass\n" +
|
||||||
|
"用法: /mission finish [子任務]\n" +
|
||||||
|
"用法: /mission running [-all]\n" +
|
||||||
|
"用法: /mission reaccept [主任務ID]\n" +
|
||||||
|
"用法: /mission finishmain [主任務ID]";
|
||||||
|
public string AllMissionsFinished => "所有任務已完成!";
|
||||||
|
public string AllRunningMissionsFinished => "共 {0} 個進行中的任務已完成!";
|
||||||
|
public string MissionFinished => "任務 {0} 已完成!";
|
||||||
|
public string InvalidMissionId => "無效的任務ID!";
|
||||||
|
public string NoRunningMissions => "沒有正在進行的任務!";
|
||||||
|
public string RunningMissions => "正在進行的任務: ";
|
||||||
|
public string PossibleStuckMissions => "可能卡住的任務: ";
|
||||||
|
public string MainMission => "主任務";
|
||||||
|
public string MissionReAccepted => "重新接受任務 {0}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Relic
|
||||||
|
/// </summary>
|
||||||
|
public class RelicTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家的遺器, 等級限製: 1 ≤ 等級 ≤ 9999";
|
||||||
|
public string Usage => "用法: /relic [遺器ID] [主詞條ID] [ID1:等級] [ID2:等級] l[等級] x[數量]";
|
||||||
|
public string RelicNotFound => "遺器不存在!";
|
||||||
|
public string InvalidMainAffixId => "主詞條ID無效!";
|
||||||
|
public string InvalidSubAffixId => "副詞條ID無效!";
|
||||||
|
public string RelicGiven => "給予玩家 @{0} {1} 個遺器 {2}, 主詞條 {3}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Reload
|
||||||
|
/// </summary>
|
||||||
|
public class ReloadTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "重新加載指定的配置";
|
||||||
|
public string Usage => "用法: /reload [banner/activity]";
|
||||||
|
public string ConfigReloaded => "配置 {0} 已重新加載!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Rogue
|
||||||
|
/// </summary>
|
||||||
|
public class RogueTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "管理模擬宇宙數據, -1意為所有已擁有祝福, buff來獲取祝福, enhance強化祝福";
|
||||||
|
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /rogue money [宇宙碎片數量]\n" +
|
||||||
|
"用法: /rogue buff [祝福ID/-1]\n" +
|
||||||
|
"用法: /rogue miracle [奇物ID]\n" +
|
||||||
|
"用法: /rogue enhance [祝福ID/-1]\n" +
|
||||||
|
"用法: /rogue unstuck - 脫離事件";
|
||||||
|
public string PlayerGainedMoney => "已獲得 {0} 宇宙碎片.";
|
||||||
|
public string PlayerGainedAllItems => "已獲得所有{0}.";
|
||||||
|
public string PlayerGainedItem => "已獲得{0} {1}.";
|
||||||
|
public string PlayerEnhancedBuff => "已強化祝福 {0}.";
|
||||||
|
public string PlayerEnhancedAllBuffs => "已強化所有祝福.";
|
||||||
|
public string PlayerUnstuck => "已脫離事件.";
|
||||||
|
public string NotFoundItem => "未找到 {0}!";
|
||||||
|
public string PlayerNotInRogue => "玩家不在模擬宇宙中!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Scene
|
||||||
|
/// </summary>
|
||||||
|
public class SceneTextCHT
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"管理玩家場景\n" +
|
||||||
|
"使用 PlaneId 預設進入指定場景\n" +
|
||||||
|
"使用 group 來獲取組, 使用 prop 來設置道具狀態, 在 PropStateEnum 獲取狀態列表\n" +
|
||||||
|
"使用 unlockall 來解鎖場景內所有道具(open狀態), 可能導致遊戲加載卡條, 使用 /scene reset 解決\n" +
|
||||||
|
"使用 reload 來重新加載當前場景, 並回到初始位置\n" +
|
||||||
|
"使用 reset 來重置指定場景所有道具狀態";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /scene [entryId]\n" +
|
||||||
|
"用法: /scene cur\n" +
|
||||||
|
"用法: /scene reload\n" +
|
||||||
|
"用法: /scene group\n" +
|
||||||
|
"用法: /scene unlockall\n" +
|
||||||
|
"用法: /scene reset [floorId]" +
|
||||||
|
"用法: /scene prop [組ID] [道具ID] [狀態]\n" +
|
||||||
|
"用法: /scene remove [實體ID]\n";
|
||||||
|
public string LoadedGroups => "已加載組: {0}.";
|
||||||
|
public string PropStateChanged => "道具: {0} 的狀態已設置為 {1}.";
|
||||||
|
public string PropNotFound => "未找到道具!";
|
||||||
|
public string EntityRemoved => "實體 {0} 已被移除.";
|
||||||
|
public string EntityNotFound => "未找到實體!";
|
||||||
|
public string AllPropsUnlocked => "所有道具已解鎖!";
|
||||||
|
public string SceneChanged => "已進入場景 {0}.";
|
||||||
|
public string SceneReloaded => "場景已重新加載!";
|
||||||
|
public string SceneReset => "已重置場景 {0} 中所有道具狀態!";
|
||||||
|
public string CurrentScene => "當前場景Entry Id: {0}, Plane Id: {1}, Floor Id: {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Mail
|
||||||
|
/// </summary>
|
||||||
|
public class MailTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "發送郵件";
|
||||||
|
public string Usage => "用法: /mail [發送名稱] [標題] [內容] [ID1:數量,ID2:數量]";
|
||||||
|
public string MailSent => "郵件已發送!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Raid
|
||||||
|
/// </summary>
|
||||||
|
public class RaidTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家的任務臨時場景";
|
||||||
|
public string Usage => "用法: /raid leave";
|
||||||
|
public string Leaved => "已離開臨時場景!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Account
|
||||||
|
/// </summary>
|
||||||
|
public class AccountTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "管理資料庫帳號";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /account create [用户名] [UID] [密碼]\n" +
|
||||||
|
"用法: /account delete [UID]";
|
||||||
|
public string InvalidUid => "UID無效!";
|
||||||
|
public string InvalidAccount => "帳號 {0} 無效!";
|
||||||
|
public string CreateSuccess => "賬號 {0} 創建成功!";
|
||||||
|
public string DeleteSuccess => "賬號 {0} 刪除成功!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Announce
|
||||||
|
/// </summary>
|
||||||
|
public class AnnounceTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "發送彈窗公告";
|
||||||
|
public string Usage => "用法: /announce [Text] [Color]";
|
||||||
|
public string SendSuccess => "發送成功!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Ban
|
||||||
|
/// </summary>
|
||||||
|
public class BanTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "封禁或解封用户";
|
||||||
|
public string Usage => "用法: /ban [add/delete]";
|
||||||
|
public string BanSuccess => "帳號已封禁!";
|
||||||
|
public string UnBanSuccess => "帳號已解封!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Unstuck
|
||||||
|
/// </summary>
|
||||||
|
public class UnstuckTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "將玩家傳送回默認場景";
|
||||||
|
public string Usage => "用法: /unstuck [UID]";
|
||||||
|
public string UnstuckSuccess => "已成功將該玩家傳送回默認場景";
|
||||||
|
public string UidNotExist => "該UID不存在!";
|
||||||
|
public string PlayerIsOnline => "該玩家目前在線上!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Setlevel
|
||||||
|
/// </summary>
|
||||||
|
public class SetlevelTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "設定玩家等級";
|
||||||
|
public string Usage => "用法: /setlevel [等級]";
|
||||||
|
public string SetlevelSuccess => "等級設定成功!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Permission
|
||||||
|
/// </summary>
|
||||||
|
public class PermissionTextCHT
|
||||||
|
{
|
||||||
|
public string Desc => "管理玩家權限";
|
||||||
|
public string Usage =>
|
||||||
|
"用法: /permission add [權限]\n" +
|
||||||
|
"用法: /permission remove [權限]\n" +
|
||||||
|
"用法: /permission clean [權限]";
|
||||||
|
public string InvalidPerm => "權限 {0} 不存在!";
|
||||||
|
public string Added => "已添加權限 {0} 到玩家 {1}!";
|
||||||
|
public string Removed => "已移除玩家 {0} 的權限 {1}!";
|
||||||
|
public string Cleaned => "已清除玩家 {0} 的所有權限!";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
570
Common/Internationalization/Message/LanguageEN.cs
Normal file
570
Common/Internationalization/Message/LanguageEN.cs
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
namespace KianaBH.Internationalization.Message;
|
||||||
|
|
||||||
|
#region Root
|
||||||
|
|
||||||
|
public class LanguageEN
|
||||||
|
{
|
||||||
|
public GameTextEN Game { get; } = new();
|
||||||
|
public ServerTextEN Server { get; } = new();
|
||||||
|
public WordTextEN Word { get; } = new(); // a placeholder for the actual word text
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 1
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game
|
||||||
|
/// </summary>
|
||||||
|
public class GameTextEN
|
||||||
|
{
|
||||||
|
public CommandTextEN Command { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server
|
||||||
|
/// </summary>
|
||||||
|
public class ServerTextEN
|
||||||
|
{
|
||||||
|
public WebTextEN Web { get; } = new();
|
||||||
|
public ServerInfoTextEN ServerInfo { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Word
|
||||||
|
/// </summary>
|
||||||
|
public class WordTextEN
|
||||||
|
{
|
||||||
|
public string Rank => "Rank";
|
||||||
|
public string Avatar => "Avatar";
|
||||||
|
public string Material => "Material";
|
||||||
|
public string Pet => "Pet";
|
||||||
|
public string Relic => "Relic";
|
||||||
|
public string Equipment => "Light Cone";
|
||||||
|
public string Talent => "Talent";
|
||||||
|
public string Banner => "Gacha";
|
||||||
|
public string Activity => "Activity";
|
||||||
|
public string CdKey => "CdKey";
|
||||||
|
public string VideoKey => "VideoKey";
|
||||||
|
public string Buff => "Blessing";
|
||||||
|
public string Miracle => "Curio";
|
||||||
|
public string Unlock => "Luxury";
|
||||||
|
public string TrainParty => "TrainParty";
|
||||||
|
|
||||||
|
// server info
|
||||||
|
public string Config => "Config File";
|
||||||
|
public string Language => "Language";
|
||||||
|
public string Log => "Log";
|
||||||
|
public string GameData => "Game Data";
|
||||||
|
public string Cache => "Resource Cache";
|
||||||
|
public string CustomData => "Custom Data";
|
||||||
|
public string Database => "Database";
|
||||||
|
public string Command => "Command";
|
||||||
|
public string SSL => "SSL";
|
||||||
|
public string Ec2b => "Ec2b";
|
||||||
|
public string SdkServer => "Web Server";
|
||||||
|
public string Handler => "Packet Handler";
|
||||||
|
public string Dispatch => "Global Dispatch";
|
||||||
|
public string Game => "Game";
|
||||||
|
public string Handbook => "Handbook";
|
||||||
|
public string NotFound => "Not Found";
|
||||||
|
public string Error => "Error";
|
||||||
|
public string FloorInfo => "Floor File";
|
||||||
|
public string FloorGroupInfo => "Floor Group File";
|
||||||
|
public string FloorMissingResult => "Teleportation and World Generation";
|
||||||
|
public string FloorGroupMissingResult => "Teleportation, Monster Battles, and World Generation";
|
||||||
|
public string Mission => "Mission";
|
||||||
|
public string MissionInfo => "Mission File";
|
||||||
|
public string SubMission => "Sub Mission";
|
||||||
|
public string SubMissionInfo => "Sub Mission File";
|
||||||
|
public string MazeSkill => "Maze Skill";
|
||||||
|
public string MazeSkillInfo => "Maze Skill File";
|
||||||
|
public string Dialogue => "Simulated Universe Event";
|
||||||
|
public string DialogueInfo => "Simulated Universe Event File";
|
||||||
|
public string Performance => "Performance";
|
||||||
|
public string PerformanceInfo => "Performance File";
|
||||||
|
public string RogueChestMap => "Simulated Universe Map";
|
||||||
|
public string RogueChestMapInfo => "Simulated Universe Map File";
|
||||||
|
public string ChessRogueRoom => "Simulated Universe DLC";
|
||||||
|
public string ChessRogueRoomInfo => "Simulated Universe DLC File";
|
||||||
|
public string SummonUnit => "Summon Unit";
|
||||||
|
public string SummonUnitInfo => "Summon Unit File";
|
||||||
|
public string RogueTournRoom => "Divergent Rogue Room";
|
||||||
|
public string RogueTournRoomInfo => "Divergent Rogue Room File";
|
||||||
|
public string TypesOfRogue => "types of rogue";
|
||||||
|
public string RogueMagicRoom => "Unknowable Domain Room";
|
||||||
|
public string RogueMagicRoomInfo => "Unknowable Domain Room File";
|
||||||
|
public string RogueDiceSurface => "Dice Surface Effect";
|
||||||
|
public string RogueDiceSurfaceInfo => "Dice Surface Effect File";
|
||||||
|
public string AdventureModifier => "AdventureModifier";
|
||||||
|
public string AdventureModifierInfo => "AdventureModifier File";
|
||||||
|
public string RogueMapGen => "RogueMapGen File";
|
||||||
|
public string RogueMiracleGroup => "RogueMiracleGroup File";
|
||||||
|
public string RogueMiracleEffectGen => "RogueMiracleEffectGen File";
|
||||||
|
|
||||||
|
public string DatabaseAccount => "Database Account";
|
||||||
|
public string Tutorial => "Tutorial";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 2
|
||||||
|
|
||||||
|
#region GameText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command
|
||||||
|
/// </summary>
|
||||||
|
public class CommandTextEN
|
||||||
|
{
|
||||||
|
public NoticeTextEN Notice { get; } = new();
|
||||||
|
|
||||||
|
public GenderTextEN Gender { get; } = new();
|
||||||
|
public AvatarTextEN Avatar { get; } = new();
|
||||||
|
public AnnounceTextEN Announce { get; } = new();
|
||||||
|
public BanTextEN Ban { get; } = new();
|
||||||
|
public GiveTextEN Give { get; } = new();
|
||||||
|
public GiveAllTextEN GiveAll { get; } = new();
|
||||||
|
public LineupTextEN Lineup { get; } = new();
|
||||||
|
public HelpTextEN Help { get; } = new();
|
||||||
|
public KickTextEN Kick { get; } = new();
|
||||||
|
public MissionTextEN Mission { get; } = new();
|
||||||
|
public RelicTextEN Relic { get; } = new();
|
||||||
|
public ReloadTextEN Reload { get; } = new();
|
||||||
|
public RogueTextEN Rogue { get; } = new();
|
||||||
|
public SceneTextEN Scene { get; } = new();
|
||||||
|
public UnlockAllTextEN UnlockAll { get; } = new();
|
||||||
|
public MailTextEN Mail { get; } = new();
|
||||||
|
public RaidTextEN Raid { get; } = new();
|
||||||
|
public AccountTextEN Account { get; } = new();
|
||||||
|
public UnstuckTextEN Unstuck { get; } = new();
|
||||||
|
public SetlevelTextEN Setlevel { get; } = new();
|
||||||
|
public PermissionTextEN Permission { get; } = new();
|
||||||
|
public RemoveItemTextEN RemoveItem { get; } = new();
|
||||||
|
public SkipTextEN Skip { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ServerTextEN
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server.Web
|
||||||
|
/// </summary>
|
||||||
|
public class WebTextEN
|
||||||
|
{
|
||||||
|
public string Maintain => "The server is undergoing maintenance, please try again later.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Server.ServerInfo
|
||||||
|
/// </summary>
|
||||||
|
public class ServerInfoTextEN
|
||||||
|
{
|
||||||
|
public string Shutdown => "Shutting down...";
|
||||||
|
public string CancelKeyPressed => "Cancel key pressed (Ctrl + C), server shutting down...";
|
||||||
|
public string StartingServer => "Starting KianaBH";
|
||||||
|
public string CurrentVersion => "Server supported versions: {0}";
|
||||||
|
public string InvalidVersion => "Unsupported game version {0}\nPlease update game to {1}";
|
||||||
|
public string LoadingItem => "Loading {0}...";
|
||||||
|
public string GeneratingItem => "Building {0}...";
|
||||||
|
public string WaitingItem => "Waiting for process {0} to complete...";
|
||||||
|
public string RegisterItem => "Registered {0} {1}(s).";
|
||||||
|
public string FailedToLoadItem => "Failed to load {0}.";
|
||||||
|
public string NewClientSecretKey => "Client Secret Key does not exist and a new Client Secret Key is being generated.";
|
||||||
|
public string FailedToInitializeItem => "Failed to initialize {0}.";
|
||||||
|
public string FailedToReadItem => "Failed to read {0}, file {1}";
|
||||||
|
public string GeneratedItem => "Generated {0}.";
|
||||||
|
public string LoadedItem => "Loaded {0}.";
|
||||||
|
public string LoadedItems => "Loaded {0} {1}(s).";
|
||||||
|
public string ServerRunning => "{0} server listening on {1}";
|
||||||
|
|
||||||
|
public string ServerStarted =>
|
||||||
|
"Startup complete! Took {0}s, better than 99% of users. Type 'help' for command help"; // This is a meme, consider localpermissiong in English
|
||||||
|
|
||||||
|
public string MissionEnabled =>
|
||||||
|
"Mission system enabled. This feature is still in development and may not work as expected. Please report any bugs to the developers.";
|
||||||
|
public string KeyStoreError => "The SSL certificate does not exist, SSL functionality has been disabled.";
|
||||||
|
public string CacheLoadSkip => "Skipped cache loading.";
|
||||||
|
|
||||||
|
public string ConfigMissing => "{0} is missing. Please check your resource folder: {1}, {2} may not be available.";
|
||||||
|
public string UnloadedItems => "Unloaded all {0}.";
|
||||||
|
public string SaveDatabase => "Database saved in {0}s";
|
||||||
|
|
||||||
|
public string WaitForAllDone =>
|
||||||
|
"You cannot enter the game yet. Please wait for all items to load before trying again";
|
||||||
|
|
||||||
|
public string UnhandledException => "An unhandled exception occurred: {0}";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layer 3
|
||||||
|
|
||||||
|
#region CommandText
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Notice
|
||||||
|
/// </summary>
|
||||||
|
public class NoticeTextEN
|
||||||
|
{
|
||||||
|
public string PlayerNotFound => "Player tidak ditemukan!";
|
||||||
|
public string InvalidArguments => "Argumen command ngawur!";
|
||||||
|
public string NoPermission => "Ga ada akses buat make command ini!";
|
||||||
|
public string CommandNotFound => "Ga ketemu itu command. Ketik '/help' untuk cek command yang ada.";
|
||||||
|
public string TargetNotFound => "Target {0} tidak ditemukan!";
|
||||||
|
public string TargetOffline => "Target {0}({1}) lagi offline! Target akan di clear.";
|
||||||
|
public string TargetFound => "Online player {0}({1}) ditemukan, command selanjutnya bakal target dia secara default.";
|
||||||
|
public string InternalError => "Error menjalankan command! {0}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Gender
|
||||||
|
/// </summary>
|
||||||
|
public class GenderTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Ganti gender & path";
|
||||||
|
public string Usage => "Pengunaan: /gender [man/woman] [id path]";
|
||||||
|
public string GenderNotSpecified => "Gender tidak ditemukan!";
|
||||||
|
public string GenderChanged => "Gender telah diganti!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.UnlockAll
|
||||||
|
/// </summary>
|
||||||
|
public class UnlockAllTextEN
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"Sesuai nama, unlock semuanya." +
|
||||||
|
"'/unlockall mission' buat finish all missions. Nanti bakal di kick setelah jalanin. Ada kemungkinan bakal stuck di tutorial, gunakan dengan hati-hati" +
|
||||||
|
"'/unlockall tutorial' buat unlock all tutorials. Nanti bakal di kick setelah jalanin. Dipakai untuk ketika stuck in some pages\n" +
|
||||||
|
"'/unlockall rogue' buat unlock all rogue types. Nanti bakal di kick setelah jalanin. Pakai bersamaan dengan '/unlockall tutorial' untuk dapatkan performance yg lebih baik";
|
||||||
|
public string Usage => "Penggunaan: /unlockall [mission/tutorial/rogue]";
|
||||||
|
public string UnlockedAll => "Unlocked All {0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Avatar
|
||||||
|
/// </summary>
|
||||||
|
public class AvatarTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Set properti untuk karakter yang dimiliki. Untuk target semua karakter, set Avatar ID ke -1 ya!";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /avatar talent [Avatar ID/-1] [Talent Level]\n" +
|
||||||
|
"Penggunaan: /avatar rank [Avatar ID/-1] [Rank]\n" +
|
||||||
|
"Penggunaan: /avatar level [Avatar ID/-1] [Avatar Level]";
|
||||||
|
public string InvalidLevel => "Invalid {0} level!";
|
||||||
|
public string AllAvatarsLevelSet => "Semua karakter {0}nya telah di set ke level {1}.";
|
||||||
|
public string AvatarLevelSet => "Karakter {0} {1}nya telah di set ke level {2}";
|
||||||
|
public string AvatarNotFound => "Karakter tidak ditemukan!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Give
|
||||||
|
/// </summary>
|
||||||
|
public class GiveTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Untuk memberi item ke player. Jangan pakai ini untuk relic.";
|
||||||
|
public string Usage => "Penggunaan: /give [item ID] l[level] x[jumlah] r[rank]";
|
||||||
|
public string ItemNotFound => "Item tidak ditemukan!";
|
||||||
|
public string GiveItem => "Telah diberi {0} {1} item {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.GiveAll
|
||||||
|
/// </summary>
|
||||||
|
public class GiveAllTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Untuk memberi player semua jenis item.";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /giveall avatar r[rank] l[level]\n" +
|
||||||
|
"Penggunaan: /giveall material x[jumlah]\n" +
|
||||||
|
"Penggunaan: /giveall equipment r[rank] l[level] x[jumlah]\n" +
|
||||||
|
"Penggunaan: /giveall relic x[jumlah]\n" +
|
||||||
|
"Penggunaan: /giveall unlock\n" +
|
||||||
|
"Penggunaan: /giveall train\n" +
|
||||||
|
"Penggunaan: /giveall path";
|
||||||
|
public string GiveAllItems => "Telah diberikan semua {0}, masing-masing berjumlah {1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Lineup
|
||||||
|
/// </summary>
|
||||||
|
public class LineupTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Atur properti overworld lineup. Mp = tech point, Sp = energy, heal ya heal.";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /lineup mp\n" +
|
||||||
|
"Penggunaan: /lineup sp\n" +
|
||||||
|
"Penggunaan: /lineup heal";
|
||||||
|
public string GainedMp => "Player telah diberikan tech points!";
|
||||||
|
public string GainedSp => "Player telah diberikan energy!";
|
||||||
|
public string HealedAllAvatars => "Satu lineup telah di heal!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Help
|
||||||
|
/// </summary>
|
||||||
|
public class HelpTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Show help information";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /help\n" +
|
||||||
|
"Penggunaan: /help [cmd]";
|
||||||
|
public string Commands => "Commands: ";
|
||||||
|
public string CommandUsage => "Penggunaan: ";
|
||||||
|
public string CommandPermission => "Level Permission Untuk Akses: ";
|
||||||
|
public string CommandAlias => "Command Alias:";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Kick
|
||||||
|
/// </summary>
|
||||||
|
public class KickTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Kick out player";
|
||||||
|
public string Usage => "Penggunaan: /kick";
|
||||||
|
public string PlayerKicked => "Player {0} telah di kick!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Mission
|
||||||
|
/// </summary>
|
||||||
|
public class MissionTextEN
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"Mengelola misi pemain\n" +
|
||||||
|
"Gunakan 'pass' untuk menyelesaikan semua misi yang sedang berjalan, perintah ini dapat menyebabkan lag yang parah, harap gunakan '/mission finish' sebagai gantinya\n" +
|
||||||
|
"Gunakan 'finish [SubMissionID]' untuk menyelesaikan sub-misi tertentu, silakan cari ID sub-misi di buku panduan\n" +
|
||||||
|
"Gunakan 'finishmain [MainMissionID]' untuk menyelesaikan misi utama tertentu, silakan cari ID misi utama di buku panduan\n" +
|
||||||
|
"Gunakan 'running <-all>' untuk melihat misi yang sedang dilacak, menambahkan '-all' akan menampilkan semua misi yang sedang berjalan dan kemungkinan misi yang macet, setelah digunakan, daftar misi yang lebih panjang mungkin muncul, harap diperhatikan\n" +
|
||||||
|
"Gunakan 'reaccept' untuk menerima kembali misi utama tertentu, silakan cari ID misi utama di buku panduan";
|
||||||
|
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /mission pass\n" +
|
||||||
|
"Penggunaan: /mission finish [ID Sub Misi]\n" +
|
||||||
|
"Penggunaan: /mission running\n" +
|
||||||
|
"Penggunaan: /mission reaccept [ID Misi Utama]\n" +
|
||||||
|
"Penggunaan: /mission finishmain [ID Misi Utama]";
|
||||||
|
|
||||||
|
public string AllMissionsFinished => "Semua tugas telah diselesaikan!";
|
||||||
|
public string AllRunningMissionsFinished => "Sebanyak {0} tugas yang sedang berjalan telah diselesaikan!";
|
||||||
|
public string MissionFinished => "Tugas {0} telah diselesaikan!";
|
||||||
|
public string InvalidMissionId => "ID tugas tidak valid!";
|
||||||
|
public string NoRunningMissions => "Tidak ada tugas yang sedang berjalan!";
|
||||||
|
public string RunningMissions => "Tugas yang sedang berjalan: ";
|
||||||
|
public string PossibleStuckMissions => "Tugas yang kemungkinan macet: ";
|
||||||
|
public string MainMission => "Tugas utama";
|
||||||
|
public string MissionReAccepted => "Tugas {0} telah diterima kembali!";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Relic
|
||||||
|
/// </summary>
|
||||||
|
public class RelicTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengelola relic pemain, Batas level: 1 ≤ Level ≤ 9999";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /relic [ID relic] [ID main affix] [ID sub affix:jumlah roll] l[level] x[jumlah]\n" +
|
||||||
|
"Catatan:\n" +
|
||||||
|
"- 'ID sub affix:jumlah roll' dapat diulang hingga 4 kali.\n" +
|
||||||
|
"- Jika tidak diberikan, sub affix akan dipilih secara acak.";
|
||||||
|
public string RelicNotFound => "Relic tidak ditemukan!";
|
||||||
|
public string InvalidMainAffixId => "ID main affix tidak valid!";
|
||||||
|
public string InvalidSubAffixId => "ID sub affix tidak valid!";
|
||||||
|
public string RelicGiven => "Memberikan kepada pemain @{0} {1} relic {2}, main affix {3}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Reload
|
||||||
|
/// </summary>
|
||||||
|
public class ReloadTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Muat ulang konfigurasi yang ditentukan";
|
||||||
|
public string Usage => "Penggunaan: /reload [banner/activity]";
|
||||||
|
public string ConfigReloaded => "Konfigurasi {0} telah dimuat ulang!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Rogue
|
||||||
|
/// </summary>
|
||||||
|
public class RogueTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Command ini tidak work. Digunakan untuk mengelola data pemain dalam simulated universe, -1 berarti semua blessing, 'buff' untuk mendapatkan blessing, 'enhance' untuk meningkatkan blessing";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /rogue money [Jumlah Universe Debris]\n" +
|
||||||
|
"Penggunaan: /rogue buff [ID blessing/-1]\n" +
|
||||||
|
"Penggunaan: /rogue miracle [ID miracle]\n" +
|
||||||
|
"Penggunaan: /rogue enhance [ID blessing/-1]\n" +
|
||||||
|
"Penggunaan: /rogue unstuck - Keluar dari event";
|
||||||
|
public string PlayerGainedMoney => "Pemain mendapatkan {0} universe debris.";
|
||||||
|
public string PlayerGainedAllItems => "Pemain mendapatkan semua {0}.";
|
||||||
|
public string PlayerGainedItem => "Pemain mendapatkan {0} {1}.";
|
||||||
|
public string PlayerEnhancedBuff => "Pemain meningkatkan blessing {0}.";
|
||||||
|
public string PlayerEnhancedAllBuffs => "Pemain meningkatkan semua blessing.";
|
||||||
|
public string PlayerUnstuck => "Pemain keluar dari event.";
|
||||||
|
public string NotFoundItem => "{0} tidak ditemukan!";
|
||||||
|
public string PlayerNotInRogue => "Pemain tidak berada dalam simulated universe!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Scene
|
||||||
|
/// </summary>
|
||||||
|
public class SceneTextEN
|
||||||
|
{
|
||||||
|
public string Desc =>
|
||||||
|
"Mengelola scene pemain\n" +
|
||||||
|
"Catatan: Sebagian besar perintah dalam grup ini digunakan untuk debugging. Pastikan Anda memahami apa yang Anda lakukan sebelum menggunakan perintah apa pun.\n" +
|
||||||
|
"Gunakan 'prop' untuk mengatur status sebuah prop. Untuk daftar status, lihat Common/Enums/Scene/PropStateEnum.cs\n" +
|
||||||
|
"Gunakan 'unlockall' untuk membuka semua prop dalam scene (misalnya, mengatur semua prop yang dapat dibuka ke status 'open'). Perintah ini dapat menyebabkan game terhenti di 90% saat memuat. Gunakan '/scene reset <floorId>' untuk mengatasi masalah ini.\n" +
|
||||||
|
"Gunakan 'change' untuk memasuki scene tertentu. Untuk EntryId, lihat Resources/MapEntrance.json\n" +
|
||||||
|
"Gunakan 'reload' untuk memuat ulang scene saat ini dan kembali ke posisi awal.\n" +
|
||||||
|
"Gunakan 'reset' untuk mengatur ulang status semua prop dalam scene yang ditentukan. Untuk mengetahui FloorId saat ini, gunakan '/scene cur'.";
|
||||||
|
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /scene [entry id]\n" +
|
||||||
|
"Penggunaan: /scene cur\n" +
|
||||||
|
"Penggunaan: /scene reload\n" +
|
||||||
|
"Penggunaan: /scene group\n" +
|
||||||
|
"Penggunaan: /scene unlockall\n" +
|
||||||
|
"Penggunaan: /scene reset [floor id]\n" +
|
||||||
|
"Penggunaan: /scene prop [group id] [prop id] [state]\n" +
|
||||||
|
"Penggunaan: /scene remove [entity id]\n";
|
||||||
|
public string LoadedGroups => "Grup yang dimuat: {0}.";
|
||||||
|
public string PropStateChanged => "Prop: {0} diatur ke status {1}.";
|
||||||
|
public string PropNotFound => "Prop tidak ditemukan!";
|
||||||
|
public string EntityRemoved => "Entity {0} telah dihapus.";
|
||||||
|
public string EntityNotFound => "Entity tidak ditemukan!";
|
||||||
|
public string AllPropsUnlocked => "Semua prop telah dibuka!";
|
||||||
|
public string SceneChanged => "Memasuki scene {0}.";
|
||||||
|
public string SceneReloaded => "Scene telah dimuat ulang!";
|
||||||
|
public string SceneReset => "Status prop di floor {0} telah direset!";
|
||||||
|
public string CurrentScene => "Scene saat ini Entry Id: {0}, Plane Id: {1}, Floor Id: {2}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Mail
|
||||||
|
/// </summary>
|
||||||
|
public class MailTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengelola mail pemain";
|
||||||
|
public string Usage => "Penggunaan: /mail [namaPengirim] [judul] [isi] [ID1:jumlah,ID2:jumlah]";
|
||||||
|
public string MailSent => "Surat telah dikirim!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Raid
|
||||||
|
/// </summary>
|
||||||
|
public class RaidTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengelola scene sementara pemain";
|
||||||
|
public string Usage => "Penggunaan: /raid leave";
|
||||||
|
public string Leaved => "Keluar dari scene sementara!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Account
|
||||||
|
/// </summary>
|
||||||
|
public class AccountTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengelola akun database";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /account create [username] [UID] [password]\n" +
|
||||||
|
"Penggunaan: /account delete [UID]";
|
||||||
|
public string InvalidUid => "Argumen UID tidak valid!";
|
||||||
|
public string InvalidAccount => "Akun {0} tidak valid!";
|
||||||
|
public string CreateSuccess => "Akun {0} berhasil dibuat!";
|
||||||
|
public string DeleteSuccess => "Akun {0} berhasil dihapus!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Announce
|
||||||
|
/// </summary>
|
||||||
|
public class AnnounceTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengirim pengumuman sistem pusat";
|
||||||
|
public string Usage => "Penggunaan: /announce [Teks] [Warna]";
|
||||||
|
public string SendSuccess => "Pengumuman berhasil dikirim!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Ban
|
||||||
|
/// </summary>
|
||||||
|
public class BanTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Blokir atau buka blokir pengguna";
|
||||||
|
public string Usage => "Penggunaan: /ban [add/delete]";
|
||||||
|
public string BanSuccess => "Akun telah diblokir!";
|
||||||
|
public string UnBanSuccess => "Akun telah dibuka blokirnya!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Unstuck
|
||||||
|
/// </summary>
|
||||||
|
public class UnstuckTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Teleport pemain kembali ke lokasi default";
|
||||||
|
public string Usage => "Penggunaan: /unstuck [UID]";
|
||||||
|
public string UnstuckSuccess => "Berhasil memindahkan pemain kembali ke lokasi default.";
|
||||||
|
public string UidNotExist => "UID tidak ditemukan!";
|
||||||
|
public string PlayerIsOnline => "Pemain sedang online!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Setlevel
|
||||||
|
/// </summary>
|
||||||
|
public class SetlevelTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengatur level pemain";
|
||||||
|
public string Usage => "Penggunaan: /setlevel [Level]";
|
||||||
|
public string SetlevelSuccess => "Berhasil mengatur level pemain!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Permission
|
||||||
|
/// </summary>
|
||||||
|
public class PermissionTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Mengelola permission pemain";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /permission add [permission]\n" +
|
||||||
|
"Penggunaan: /permission remove [permission]\n" +
|
||||||
|
"Penggunaan: /permission clean [permission]";
|
||||||
|
public string InvalidPerm => "Permission {0} tidak ditemukan!";
|
||||||
|
public string Added => "Menambahkan permission {0} ke pemain {1}!";
|
||||||
|
public string Removed => "Menghapus permission {1} dari pemain {0}!";
|
||||||
|
public string Cleaned => "Menghapus semua permission dari pemain {0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.RemoveItem
|
||||||
|
/// </summary>
|
||||||
|
public class RemoveItemTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Menghapus Relic atau Lightcone";
|
||||||
|
public string Usage =>
|
||||||
|
"Penggunaan: /remove relic\n" +
|
||||||
|
"Penggunaan: /remove lightcone";
|
||||||
|
public string InvalidPerm => "Permission {0} tidak ditemukan!";
|
||||||
|
public string RemovedRelics => "Relic telah dihapus dari pemain {0}!";
|
||||||
|
public string RemovedLightcones => "Lightcone telah dihapus dari pemain {0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// path: Game.Command.Skip
|
||||||
|
/// </summary>
|
||||||
|
public class SkipTextEN
|
||||||
|
{
|
||||||
|
public string Desc => "Skip 1st half MOC / PF / AS dan langsung ke 2nd half";
|
||||||
|
public string Usage => "Penggunaan: /skip" ;
|
||||||
|
public string Success => "Berhasil menyetel skip 1st half ke {0}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
9
Common/Internationalization/PluginLanguageAttribute.cs
Normal file
9
Common/Internationalization/PluginLanguageAttribute.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using KianaBH.Enums.Language;
|
||||||
|
|
||||||
|
namespace KianaBH.Internationalization;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class PluginLanguageAttribute(ProgramLanguageTypeEnum languageType) : Attribute
|
||||||
|
{
|
||||||
|
public ProgramLanguageTypeEnum LanguageType { get; } = languageType;
|
||||||
|
}
|
||||||
99
Common/Util/ConfigManager.cs
Normal file
99
Common/Util/ConfigManager.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using KianaBH.Configuration;
|
||||||
|
using KianaBH.Internationalization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
|
||||||
|
namespace KianaBH.Util;
|
||||||
|
|
||||||
|
public static class ConfigManager
|
||||||
|
{
|
||||||
|
public static readonly Logger Logger = new("ConfigManager");
|
||||||
|
public static ConfigContainer Config { get; private set; } = new();
|
||||||
|
private static readonly string ConfigFilePath = Config.Path.ConfigPath + "/Config.json";
|
||||||
|
public static HotfixContainer Hotfix { get; private set; } = new();
|
||||||
|
private static readonly string HotfixFilePath = Config.Path.ConfigPath + "/Hotfix.json";
|
||||||
|
|
||||||
|
public static void LoadConfig()
|
||||||
|
{
|
||||||
|
LoadConfigData();
|
||||||
|
LoadHotfixData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadConfigData()
|
||||||
|
{
|
||||||
|
var file = new FileInfo(ConfigFilePath);
|
||||||
|
if (!file.Exists)
|
||||||
|
{
|
||||||
|
Config = new()
|
||||||
|
{
|
||||||
|
ServerOption =
|
||||||
|
{
|
||||||
|
Language = Extensions.Extensions.GetCurrentLanguage()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Logger.Info("Current Language is " + Config.ServerOption.Language);
|
||||||
|
SaveData(Config, ConfigFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
|
using (var reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
var json = reader.ReadToEnd();
|
||||||
|
Config = JsonConvert.DeserializeObject<ConfigContainer>(json)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveData(Config, ConfigFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadHotfixData()
|
||||||
|
{
|
||||||
|
var file = new FileInfo(HotfixFilePath);
|
||||||
|
|
||||||
|
// Generate all necessary versions
|
||||||
|
var verList = Extensions.Extensions.GetSupportVersions();
|
||||||
|
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.CurrentVersion",
|
||||||
|
verList.Aggregate((current, next) => $"{current}, {next}")));
|
||||||
|
|
||||||
|
if (!file.Exists)
|
||||||
|
{
|
||||||
|
Hotfix = new HotfixContainer();
|
||||||
|
SaveData(Hotfix, HotfixFilePath);
|
||||||
|
file.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
|
using (var reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
var json = reader.ReadToEnd();
|
||||||
|
Hotfix = JsonConvert.DeserializeObject<HotfixContainer>(json)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var version in verList)
|
||||||
|
if (!Hotfix.Hotfixes.TryGetValue(version, out var _))
|
||||||
|
Hotfix.Hotfixes[version] = new();
|
||||||
|
|
||||||
|
SaveData(Hotfix, HotfixFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SaveData(object data, string path)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||||
|
using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||||
|
using var writer = new StreamWriter(stream);
|
||||||
|
writer.Write(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitDirectories()
|
||||||
|
{
|
||||||
|
foreach (var property in Config.Path.GetType().GetProperties())
|
||||||
|
{
|
||||||
|
var dir = property.GetValue(Config.Path)?.ToString();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(dir))
|
||||||
|
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Common/Util/Crpyto/DispatchEncryption.cs
Normal file
35
Common/Util/Crpyto/DispatchEncryption.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace KianaBH.Util.Crypto;
|
||||||
|
|
||||||
|
public static class DispatchEncryption
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string? EncryptDispatchContent(string version, object? data)
|
||||||
|
{
|
||||||
|
if (!ConfigManager.Hotfix.AesKeys.TryGetValue(version, out var aesKey))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var serializedData = JsonSerializer.Serialize(data, JsonSerializerOptions);
|
||||||
|
var keyBytes = aesKey.Split(' ')
|
||||||
|
.Select(b => Convert.ToByte(b, 16))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
using var aes = Aes.Create();
|
||||||
|
aes.Mode = CipherMode.ECB;
|
||||||
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
|
aes.Key = keyBytes;
|
||||||
|
|
||||||
|
var encryptor = aes.CreateEncryptor();
|
||||||
|
var dataBytes = Encoding.UTF8.GetBytes(serializedData);
|
||||||
|
var encryptedBytes = encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length);
|
||||||
|
|
||||||
|
return Convert.ToBase64String(encryptedBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
232
Common/Util/Extensions/Extensions.cs
Normal file
232
Common/Util/Extensions/Extensions.cs
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
using KianaBH.Proto;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace KianaBH.Util.Extensions;
|
||||||
|
|
||||||
|
public static partial class Extensions
|
||||||
|
{
|
||||||
|
#region Regex
|
||||||
|
|
||||||
|
[GeneratedRegex(@"CN|OS|BETA|PROD|CECREATION|Android|Win|iOS")]
|
||||||
|
public static partial Regex VersionRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?<=Avatar_)(.*?)(?=_Config)")]
|
||||||
|
public static partial Regex AvatarConfigRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?<=Avatar_RogueBattleevent)(.*?)(?=_Config.json)")]
|
||||||
|
public static partial Regex BattleEventDataRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"coin(\d+)tier")]
|
||||||
|
public static partial Regex ProductRegex();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static string GetCurrentLanguage()
|
||||||
|
{
|
||||||
|
var uiCulture = CultureInfo.CurrentUICulture;
|
||||||
|
return uiCulture.Name switch
|
||||||
|
{
|
||||||
|
"zh-CN" => "CHS",
|
||||||
|
"zh-TW" => "CHT",
|
||||||
|
"ja-JP" => "JP",
|
||||||
|
_ => "EN"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> GetSupportVersions()
|
||||||
|
{
|
||||||
|
var verList = new List<string>();
|
||||||
|
if (GameConstants.GAME_VERSION[^1] == '5')
|
||||||
|
for (var i = 1; i < 6; i++)
|
||||||
|
verList.Add(GameConstants.GAME_VERSION + i.ToString());
|
||||||
|
else
|
||||||
|
verList.Add(GameConstants.GAME_VERSION);
|
||||||
|
|
||||||
|
return verList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T RandomElement<T>(this List<T> values)
|
||||||
|
{
|
||||||
|
var index = new Random().Next(values.Count);
|
||||||
|
return values[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RandomKey(int length)
|
||||||
|
{
|
||||||
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
var random = new Random();
|
||||||
|
return new string(Enumerable.Repeat(chars, length)
|
||||||
|
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ICollection<T> Clone<T>(this ICollection<T> values)
|
||||||
|
{
|
||||||
|
List<T> list = [.. values];
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int RandomInt(int from, int to)
|
||||||
|
{
|
||||||
|
return new Random().Next(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetSha256Hash(string input)
|
||||||
|
{
|
||||||
|
byte[] bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input));
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < bytes.Length; i++) builder.Append(bytes[i].ToString("x2"));
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SafeAdd<T>(this List<T> list, T item)
|
||||||
|
{
|
||||||
|
if (!list.Contains(item)) list.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SafeAddRange<T>(this List<T> list, List<T> item)
|
||||||
|
{
|
||||||
|
foreach (var i in item) list.SafeAdd(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long GetUnixSec()
|
||||||
|
{
|
||||||
|
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ToUnixSec(this DateTime dt)
|
||||||
|
{
|
||||||
|
return new DateTimeOffset(dt).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long GetUnixMs()
|
||||||
|
{
|
||||||
|
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToArrayString<T>(this List<T> list)
|
||||||
|
{
|
||||||
|
return list.JoinFormat(", ", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToJsonString<TK, TV>(this Dictionary<TK, TV> dic) where TK : notnull
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(dic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] StringToByteArray(string hex)
|
||||||
|
{
|
||||||
|
if (hex.Length % 2 == 1)
|
||||||
|
throw new Exception("The binary key cannot have an odd number of digits");
|
||||||
|
|
||||||
|
byte[] arr = new byte[hex.Length >> 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < hex.Length >> 1; ++i)
|
||||||
|
{
|
||||||
|
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetHexVal(char hex)
|
||||||
|
{
|
||||||
|
int val = (int)hex;
|
||||||
|
//For uppercase A-F letters:
|
||||||
|
//return val - (val < 58 ? 48 : 55);
|
||||||
|
//For lowercase a-f letters:
|
||||||
|
//return val - (val < 58 ? 48 : 87);
|
||||||
|
//Or the two combined, but a bit slower:
|
||||||
|
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Kcp Utils
|
||||||
|
|
||||||
|
public static string JoinFormat<T>(this IEnumerable<T> list, string separator,
|
||||||
|
string formatString)
|
||||||
|
{
|
||||||
|
formatString = string.IsNullOrWhiteSpace(formatString) ? "{0}" : formatString;
|
||||||
|
return string.Join(separator,
|
||||||
|
list.Select(item => string.Format(formatString, item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteConvID(this BinaryWriter bw, long convId)
|
||||||
|
{
|
||||||
|
//bw.Write(convId);
|
||||||
|
bw.Write((int)(convId >> 32));
|
||||||
|
bw.Write((int)(convId & 0xFFFFFFFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long GetNextAvailableIndex<T>(this SortedList<long, T> sortedList)
|
||||||
|
{
|
||||||
|
long key = 1;
|
||||||
|
long count = sortedList.Count;
|
||||||
|
long counter = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (count == 0) break;
|
||||||
|
var nextKeyInList = sortedList.Keys.ElementAt((Index)counter++);
|
||||||
|
if (key != nextKeyInList) break;
|
||||||
|
key = nextKeyInList + 1;
|
||||||
|
} while (count != 1 && counter != count && key == sortedList.Keys.ElementAt((Index)counter));
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long AddNext<T>(this SortedList<long, T> sortedList, T item)
|
||||||
|
{
|
||||||
|
var key = sortedList.GetNextAvailableIndex();
|
||||||
|
sortedList.Add(key, item);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ReadInt32BE(this BinaryReader br)
|
||||||
|
{
|
||||||
|
return BinaryPrimitives.ReadInt32BigEndian(br.ReadBytes(sizeof(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint ReadUInt32BE(this BinaryReader br)
|
||||||
|
{
|
||||||
|
return BinaryPrimitives.ReadUInt32BigEndian(br.ReadBytes(sizeof(uint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ushort ReadUInt16BE(this BinaryReader br)
|
||||||
|
{
|
||||||
|
return BinaryPrimitives.ReadUInt16BigEndian(br.ReadBytes(sizeof(ushort)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteUInt16BE(this BinaryWriter bw, ushort value)
|
||||||
|
{
|
||||||
|
Span<byte> data = stackalloc byte[sizeof(ushort)];
|
||||||
|
BinaryPrimitives.WriteUInt16BigEndian(data, value);
|
||||||
|
bw.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteInt32BE(this BinaryWriter bw, int value)
|
||||||
|
{
|
||||||
|
Span<byte> data = stackalloc byte[sizeof(int)];
|
||||||
|
BinaryPrimitives.WriteInt32BigEndian(data, value);
|
||||||
|
bw.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteUInt32BE(this BinaryWriter bw, uint value)
|
||||||
|
{
|
||||||
|
Span<byte> data = stackalloc byte[sizeof(uint)];
|
||||||
|
BinaryPrimitives.WriteUInt32BigEndian(data, value);
|
||||||
|
bw.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteUInt64BE(this BinaryWriter bw, ulong value)
|
||||||
|
{
|
||||||
|
Span<byte> data = stackalloc byte[sizeof(ulong)];
|
||||||
|
BinaryPrimitives.WriteUInt64BigEndian(data, value);
|
||||||
|
bw.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
24
Common/Util/Extensions/JsonStringToObjectConverter.cs
Normal file
24
Common/Util/Extensions/JsonStringToObjectConverter.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace KianaBH.Util.Extensions;
|
||||||
|
|
||||||
|
public class JsonStringToObjectConverter<T> : JsonConverter<T> where T : class
|
||||||
|
{
|
||||||
|
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType != JsonTokenType.String)
|
||||||
|
return JsonSerializer.Deserialize<T>(ref reader, options);
|
||||||
|
|
||||||
|
var jsonString = reader.GetString();
|
||||||
|
return !string.IsNullOrEmpty(jsonString)
|
||||||
|
? JsonSerializer.Deserialize<T>(jsonString, options)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(value, options);
|
||||||
|
writer.WriteStringValue(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Common/Util/GameConstants.cs
Normal file
11
Common/Util/GameConstants.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace KianaBH.Util;
|
||||||
|
|
||||||
|
public static class GameConstants
|
||||||
|
{
|
||||||
|
public const string GAME_VERSION = "8.2.0";
|
||||||
|
public const int MAX_STAMINA = 300;
|
||||||
|
public const int STAMINA_RECOVERY_TIME = 360; // 6 minutes
|
||||||
|
public const int STAMINA_RESERVE_RECOVERY_TIME = 1080; // 18 minutes
|
||||||
|
public const int INVENTORY_MAX_EQUIPMENT = 1000;
|
||||||
|
public const int MAX_LINEUP_COUNT = 9;
|
||||||
|
}
|
||||||
185
Common/Util/IConsole.cs
Normal file
185
Common/Util/IConsole.cs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
using Kodnix.Character;
|
||||||
|
|
||||||
|
namespace KianaBH.Util;
|
||||||
|
|
||||||
|
public class IConsole
|
||||||
|
{
|
||||||
|
public static readonly string PrefixContent = "[KianaBH]> ";
|
||||||
|
public static readonly string Prefix = $"\u001b[38;2;255;192;203m{PrefixContent}\u001b[0m";
|
||||||
|
private static readonly int HistoryMaxCount = 10;
|
||||||
|
|
||||||
|
public static List<char> Input { get; set; } = [];
|
||||||
|
private static int CursorIndex { get; set; } = 0;
|
||||||
|
private static readonly List<string> InputHistory = [];
|
||||||
|
private static int HistoryIndex = -1;
|
||||||
|
|
||||||
|
public static event Action<string>? OnConsoleExcuteCommand;
|
||||||
|
|
||||||
|
public static void InitConsole()
|
||||||
|
{
|
||||||
|
Console.Title = ConfigManager.Config.GameServer.GameServerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetWidth(string str)
|
||||||
|
=> str.ToCharArray().Sum(EastAsianWidth.GetLength);
|
||||||
|
|
||||||
|
public static void RedrawInput(List<char> input, bool hasPrefix = true)
|
||||||
|
=> RedrawInput(new string([.. input]), hasPrefix);
|
||||||
|
|
||||||
|
public static void RedrawInput(string input, bool hasPrefix = true)
|
||||||
|
{
|
||||||
|
var length = GetWidth(input);
|
||||||
|
if (hasPrefix)
|
||||||
|
{
|
||||||
|
input = Prefix + input;
|
||||||
|
length += GetWidth(PrefixContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Console.GetCursorPosition().Left > 0)
|
||||||
|
Console.SetCursorPosition(0, Console.CursorTop);
|
||||||
|
|
||||||
|
Console.Write(input + new string(' ', Console.BufferWidth - length));
|
||||||
|
Console.SetCursorPosition(length, Console.CursorTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Handlers
|
||||||
|
|
||||||
|
public static void HandleEnter()
|
||||||
|
{
|
||||||
|
var input = new string([.. Input]);
|
||||||
|
if (string.IsNullOrWhiteSpace(input)) return;
|
||||||
|
|
||||||
|
// New line
|
||||||
|
Console.WriteLine();
|
||||||
|
Input = [];
|
||||||
|
CursorIndex = 0;
|
||||||
|
if (InputHistory.Count >= HistoryMaxCount)
|
||||||
|
InputHistory.RemoveAt(0);
|
||||||
|
InputHistory.Add(input);
|
||||||
|
HistoryIndex = InputHistory.Count;
|
||||||
|
|
||||||
|
// Handle command
|
||||||
|
if (input.StartsWith('/')) input = input[1..].Trim();
|
||||||
|
OnConsoleExcuteCommand?.Invoke(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleBackspace()
|
||||||
|
{
|
||||||
|
if (CursorIndex <= 0) return;
|
||||||
|
CursorIndex--;
|
||||||
|
var targetWidth = GetWidth(Input[CursorIndex].ToString());
|
||||||
|
Input.RemoveAt(CursorIndex);
|
||||||
|
|
||||||
|
var (left, _) = Console.GetCursorPosition();
|
||||||
|
Console.SetCursorPosition(left - targetWidth, Console.CursorTop);
|
||||||
|
var remain = new string([.. Input.Skip(CursorIndex)]);
|
||||||
|
Console.Write(remain + new string(' ', targetWidth));
|
||||||
|
Console.SetCursorPosition(left - targetWidth, Console.CursorTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleUpArrow()
|
||||||
|
{
|
||||||
|
if (InputHistory.Count == 0) return;
|
||||||
|
|
||||||
|
if (HistoryIndex > 0)
|
||||||
|
{
|
||||||
|
HistoryIndex--;
|
||||||
|
var history = InputHistory[HistoryIndex];
|
||||||
|
Input = [.. history];
|
||||||
|
CursorIndex = Input.Count;
|
||||||
|
RedrawInput(Input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleDownArrow()
|
||||||
|
{
|
||||||
|
if (HistoryIndex >= InputHistory.Count) return;
|
||||||
|
|
||||||
|
HistoryIndex++;
|
||||||
|
if (HistoryIndex >= InputHistory.Count)
|
||||||
|
{
|
||||||
|
HistoryIndex = InputHistory.Count;
|
||||||
|
Input = [];
|
||||||
|
CursorIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var history = InputHistory[HistoryIndex];
|
||||||
|
Input = [.. history];
|
||||||
|
CursorIndex = Input.Count;
|
||||||
|
}
|
||||||
|
RedrawInput(Input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleLeftArrow()
|
||||||
|
{
|
||||||
|
if (CursorIndex <= 0) return;
|
||||||
|
|
||||||
|
var (left, _) = Console.GetCursorPosition();
|
||||||
|
CursorIndex--;
|
||||||
|
Console.SetCursorPosition(left - GetWidth(Input[CursorIndex].ToString()), Console.CursorTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleRightArrow()
|
||||||
|
{
|
||||||
|
if (CursorIndex >= Input.Count) return;
|
||||||
|
|
||||||
|
var (left, _) = Console.GetCursorPosition();
|
||||||
|
CursorIndex++;
|
||||||
|
Console.SetCursorPosition(left + GetWidth(Input[CursorIndex - 1].ToString()), Console.CursorTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleInput(ConsoleKeyInfo keyInfo)
|
||||||
|
{
|
||||||
|
if (char.IsControl(keyInfo.KeyChar)) return;
|
||||||
|
if (Input.Count >= (Console.BufferWidth - PrefixContent.Length)) return;
|
||||||
|
HandleInput(keyInfo.KeyChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleInput(char keyChar)
|
||||||
|
{
|
||||||
|
Input.Insert(CursorIndex, keyChar);
|
||||||
|
CursorIndex++;
|
||||||
|
|
||||||
|
var (left, _) = Console.GetCursorPosition();
|
||||||
|
Console.Write(new string([.. Input.Skip(CursorIndex - 1)]));
|
||||||
|
Console.SetCursorPosition(left + GetWidth(keyChar.ToString()), Console.CursorTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static string ListenConsole()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
ConsoleKeyInfo keyInfo;
|
||||||
|
try { keyInfo = Console.ReadKey(true); }
|
||||||
|
catch (InvalidOperationException) { continue; }
|
||||||
|
|
||||||
|
switch (keyInfo.Key)
|
||||||
|
{
|
||||||
|
case ConsoleKey.Enter:
|
||||||
|
HandleEnter();
|
||||||
|
break;
|
||||||
|
case ConsoleKey.Backspace:
|
||||||
|
HandleBackspace();
|
||||||
|
break;
|
||||||
|
case ConsoleKey.LeftArrow:
|
||||||
|
HandleLeftArrow();
|
||||||
|
break;
|
||||||
|
case ConsoleKey.RightArrow:
|
||||||
|
HandleRightArrow();
|
||||||
|
break;
|
||||||
|
case ConsoleKey.UpArrow:
|
||||||
|
HandleUpArrow();
|
||||||
|
break;
|
||||||
|
case ConsoleKey.DownArrow:
|
||||||
|
HandleDownArrow();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
HandleInput(keyInfo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
109
Common/Util/Logger.cs
Normal file
109
Common/Util/Logger.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace KianaBH.Util;
|
||||||
|
|
||||||
|
public class Logger(string moduleName)
|
||||||
|
{
|
||||||
|
private static FileInfo? LogFile;
|
||||||
|
private static readonly object _lock = new();
|
||||||
|
private readonly string ModuleName = moduleName;
|
||||||
|
|
||||||
|
public void Log(string message, LoggerLevel level)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
var savedInput = IConsole.Input.ToList(); // Copy
|
||||||
|
IConsole.RedrawInput("", false);
|
||||||
|
AnsiConsole.MarkupLine($"[[[bold deepskyblue3_1]{DateTime.Now:HH:mm:ss}[/]]] " +
|
||||||
|
$"[[[gray]{ModuleName}[/]]] [[[{(ConsoleColor)level}]{level}[/]]] " +
|
||||||
|
$"{message.Replace("[", "[[").Replace("]", "]]")}");
|
||||||
|
IConsole.RedrawInput(savedInput);
|
||||||
|
|
||||||
|
var logMessage = $"[{DateTime.Now:HH:mm:ss}] [{ModuleName}] [{level}] {message}";
|
||||||
|
WriteToFile(logMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Info(string message, Exception? e = null)
|
||||||
|
{
|
||||||
|
Log(message, LoggerLevel.INFO);
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
Log(e.Message, LoggerLevel.INFO);
|
||||||
|
Log(e.StackTrace!, LoggerLevel.INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warn(string message, Exception? e = null)
|
||||||
|
{
|
||||||
|
Log(message, LoggerLevel.WARN);
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
Log(e.Message, LoggerLevel.WARN);
|
||||||
|
Log(e.StackTrace!, LoggerLevel.WARN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(string message, Exception? e = null)
|
||||||
|
{
|
||||||
|
Log(message, LoggerLevel.ERROR);
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
Log(e.Message, LoggerLevel.ERROR);
|
||||||
|
Log(e.StackTrace!, LoggerLevel.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fatal(string message, Exception? e = null)
|
||||||
|
{
|
||||||
|
Log(message, LoggerLevel.FATAL);
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
Log(e.Message, LoggerLevel.FATAL);
|
||||||
|
Log(e.StackTrace!, LoggerLevel.FATAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Debug(string message, Exception? e = null)
|
||||||
|
{
|
||||||
|
Log(message, LoggerLevel.DEBUG);
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
Log(e.Message, LoggerLevel.DEBUG);
|
||||||
|
Log(e.StackTrace!, LoggerLevel.DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetLogFile(FileInfo file)
|
||||||
|
{
|
||||||
|
LogFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteToFile(string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (LogFile == null) throw new Exception("LogFile is not set");
|
||||||
|
using var sw = LogFile.AppendText();
|
||||||
|
sw.WriteLine(message);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Logger GetByClassName()
|
||||||
|
{
|
||||||
|
return new Logger(new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LoggerLevel
|
||||||
|
{
|
||||||
|
INFO = ConsoleColor.Cyan,
|
||||||
|
WARN = ConsoleColor.Yellow,
|
||||||
|
ERROR = ConsoleColor.Red,
|
||||||
|
FATAL = ConsoleColor.DarkRed,
|
||||||
|
DEBUG = ConsoleColor.Blue
|
||||||
|
}
|
||||||
34
Common/Util/LoggingMiddleware.cs
Normal file
34
Common/Util/LoggingMiddleware.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using KianaBH.Util;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace KianaBH.SdkServer.Utils;
|
||||||
|
|
||||||
|
public class RequestLoggingMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
public async Task InvokeAsync(HttpContext context, Logger logger)
|
||||||
|
{
|
||||||
|
var request = context.Request;
|
||||||
|
var method = request.Method;
|
||||||
|
var path = request.Path + request.QueryString;
|
||||||
|
|
||||||
|
await next(context);
|
||||||
|
|
||||||
|
var statusCode = context.Response.StatusCode;
|
||||||
|
|
||||||
|
if (path.StartsWith("/report") || path.Contains("/log/") || path == "/alive")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (statusCode == 200)
|
||||||
|
{
|
||||||
|
logger.Info($"{method} {path} => {statusCode}");
|
||||||
|
}
|
||||||
|
else if (statusCode == 404)
|
||||||
|
{
|
||||||
|
logger.Warn($"{method} {path} => {statusCode}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error($"{method} {path} => {statusCode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Common/Util/Security/Crypto.cs
Normal file
29
Common/Util/Security/Crypto.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace KianaBH.Util.Security;
|
||||||
|
|
||||||
|
public class Crypto
|
||||||
|
{
|
||||||
|
private static readonly Random SecureRandom = new();
|
||||||
|
|
||||||
|
// Simple way to create a unique session key
|
||||||
|
public static string CreateSessionKey(string accountUid)
|
||||||
|
{
|
||||||
|
var random = new byte[64];
|
||||||
|
SecureRandom.NextBytes(random);
|
||||||
|
|
||||||
|
var temp = accountUid + "." + DateTime.Now.Ticks + "." + SecureRandom;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var bytes = SHA512.HashData(Encoding.UTF8.GetBytes(temp));
|
||||||
|
return Convert.ToBase64String(bytes);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
var bytes = SHA512.HashData(Encoding.UTF8.GetBytes(temp));
|
||||||
|
return Convert.ToBase64String(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Config/Config.json
Normal file
64
Config/Config.json
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"HttpServer": {
|
||||||
|
"BindAddress": "0.0.0.0",
|
||||||
|
"PublicAddress": "127.0.0.1",
|
||||||
|
"Port": 619,
|
||||||
|
"UseSSL": true
|
||||||
|
},
|
||||||
|
"Key": {
|
||||||
|
"KeyStoreFile": "FireflyDH.pfx",
|
||||||
|
"KeyStorePassword": "lethe",
|
||||||
|
"Ec2bKeyFile": "ClientSecretKey.ec2b"
|
||||||
|
},
|
||||||
|
"GameServer": {
|
||||||
|
"BindAddress": "0.0.0.0",
|
||||||
|
"PublicAddress": "127.0.0.1",
|
||||||
|
"Port": 2007,
|
||||||
|
"KcpAliveMs": 45000,
|
||||||
|
"DatabaseName": "firefly.db",
|
||||||
|
"GameServerId": "FireflyDH",
|
||||||
|
"GameServerName": "FireflyDH",
|
||||||
|
"UsePacketEncryption": true
|
||||||
|
},
|
||||||
|
"Path": {
|
||||||
|
"ResourcePath": "Resources",
|
||||||
|
"ConfigPath": "Config",
|
||||||
|
"DatabasePath": "Config/Database",
|
||||||
|
"HandbookPath": "Config/Handbook",
|
||||||
|
"LogPath": "Config/Logs",
|
||||||
|
"KeyPath": "Config/Keys",
|
||||||
|
"DataPath": "Config/Data"
|
||||||
|
},
|
||||||
|
"ServerOption": {
|
||||||
|
"EnableMission": false,
|
||||||
|
"DefaultGender": "Woman",
|
||||||
|
"Language": "EN",
|
||||||
|
"FallbackLanguage": "EN",
|
||||||
|
"Region": "BETAOVERSEA",
|
||||||
|
"DefaultPermissions": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"ServerBanner": {
|
||||||
|
"EnableBanner": true,
|
||||||
|
"Content": "Welcome to FireflyDH!",
|
||||||
|
"AdminContent": "Welcome to FireflyDH (Admin Access)!"
|
||||||
|
},
|
||||||
|
"ServerProfile": {
|
||||||
|
"Name": "Server",
|
||||||
|
"Uid": 80,
|
||||||
|
"Signature": "Type /help for a list of commands",
|
||||||
|
"Level": 1,
|
||||||
|
"HeadIcon": 200105,
|
||||||
|
"ChatBubbleId": 220001,
|
||||||
|
"DisplayAvatarId": 1001,
|
||||||
|
"DisplayAvatarLevel": 1
|
||||||
|
},
|
||||||
|
"AutoCreateUser": true,
|
||||||
|
"UseRealPassword": true,
|
||||||
|
"SavePersonalDebugFile": false,
|
||||||
|
"UseCache": true
|
||||||
|
},
|
||||||
|
"MuipServer": {
|
||||||
|
"AdminKey": "8f52aa3f-ad56-4d48-acc8-651151e4d989"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Config/Hotfix.json
Normal file
35
Config/Hotfix.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Region": "None",
|
||||||
|
"HotfixData": {
|
||||||
|
"3.1.51": {
|
||||||
|
"AssetBundleUrl": "",
|
||||||
|
"ExResourceUrl": "",
|
||||||
|
"LuaUrl": "",
|
||||||
|
"IfixUrl": ""
|
||||||
|
},
|
||||||
|
"3.1.52": {
|
||||||
|
"AssetBundleUrl": "",
|
||||||
|
"ExResourceUrl": "",
|
||||||
|
"LuaUrl": "",
|
||||||
|
"IfixUrl": ""
|
||||||
|
},
|
||||||
|
"3.1.53": {
|
||||||
|
"AssetBundleUrl": "",
|
||||||
|
"ExResourceUrl": "",
|
||||||
|
"LuaUrl": "",
|
||||||
|
"IfixUrl": ""
|
||||||
|
},
|
||||||
|
"3.1.54": {
|
||||||
|
"AssetBundleUrl": "",
|
||||||
|
"ExResourceUrl": "",
|
||||||
|
"LuaUrl": "",
|
||||||
|
"IfixUrl": ""
|
||||||
|
},
|
||||||
|
"3.1.55": {
|
||||||
|
"AssetBundleUrl": "",
|
||||||
|
"ExResourceUrl": "",
|
||||||
|
"LuaUrl": "",
|
||||||
|
"IfixUrl": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
GameServer/Command/CommandArg.cs
Normal file
79
GameServer/Command/CommandArg.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using KianaBH.Database.Account;
|
||||||
|
using KianaBH.GameServer.Server;
|
||||||
|
using KianaBH.Internationalization;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Command;
|
||||||
|
|
||||||
|
public class CommandArg
|
||||||
|
{
|
||||||
|
public string RawArg { get; } = "";
|
||||||
|
public List<string> Args { get; } = [];
|
||||||
|
public List<string> Attributes { get; } = [];
|
||||||
|
public ICommandSender Sender { get; }
|
||||||
|
public int TargetUid { get; set; } = 0;
|
||||||
|
public Connection? Target { get; set; }
|
||||||
|
|
||||||
|
public CommandArg(string rawArg, ICommandSender sender)
|
||||||
|
{
|
||||||
|
Sender = sender;
|
||||||
|
RawArg = rawArg;
|
||||||
|
foreach (var arg in rawArg.Split(' '))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(arg)) continue;
|
||||||
|
Args.Add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask SendMsg(string msg)
|
||||||
|
{
|
||||||
|
await Sender.SendMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetInt(int index)
|
||||||
|
{
|
||||||
|
if (Args.Count <= index) return 0;
|
||||||
|
if (int.TryParse(Args[index], out var res))
|
||||||
|
return res;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<int?> GetOption(char pre, string def = "1")
|
||||||
|
{
|
||||||
|
var opStr = Args.FirstOrDefault(x => x[0] == pre)?[1..] ?? def;
|
||||||
|
if (!int.TryParse(opStr, out var op))
|
||||||
|
{
|
||||||
|
await SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<bool> CheckArgCnt(int start, int? end = null)
|
||||||
|
{
|
||||||
|
end ??= start;
|
||||||
|
if (Args.Count >= start && Args.Count <= end) return true;
|
||||||
|
await SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<bool> CheckTarget()
|
||||||
|
{
|
||||||
|
if (AccountData.GetAccountByUid(TargetUid) == null)
|
||||||
|
{
|
||||||
|
await SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<bool> CheckOnlineTarget(bool sendMsg = true)
|
||||||
|
{
|
||||||
|
if (Target == null)
|
||||||
|
{
|
||||||
|
if (sendMsg)
|
||||||
|
await SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
GameServer/Command/CommandAttribute.cs
Normal file
25
GameServer/Command/CommandAttribute.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using KianaBH.Enums.Player;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Command;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class CommandInfoAttribute(
|
||||||
|
string name, string desc, string usage, string[] alias, PermEnum[] perm) : Attribute
|
||||||
|
{
|
||||||
|
public string Name { get; } = name;
|
||||||
|
public string Description { get; } = desc;
|
||||||
|
public string Usage { get; } = usage;
|
||||||
|
public PermEnum[] Perm { get; } = perm;
|
||||||
|
public string[] Alias { get; } = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class CommandDefaultAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class CommandMethodAttribute(string method) : Attribute
|
||||||
|
{
|
||||||
|
public string MethodName { get; } = method;
|
||||||
|
}
|
||||||
19
GameServer/Command/CommandExecutor.cs
Normal file
19
GameServer/Command/CommandExecutor.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
namespace KianaBH.GameServer.Command;
|
||||||
|
|
||||||
|
public static class CommandExecutor
|
||||||
|
{
|
||||||
|
public delegate void RunCommand(ICommandSender sender, string cmd);
|
||||||
|
|
||||||
|
public static event RunCommand? OnRunCommand;
|
||||||
|
|
||||||
|
public static void ExecuteCommand(ICommandSender sender, string cmd)
|
||||||
|
{
|
||||||
|
OnRunCommand?.Invoke(sender, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConsoleExcuteCommand(string input)
|
||||||
|
{
|
||||||
|
CommandManager.HandleCommand(input, new ConsoleCommandSender(CommandManager.Logger));
|
||||||
|
}
|
||||||
|
}
|
||||||
3
GameServer/Command/CommandInterface.cs
Normal file
3
GameServer/Command/CommandInterface.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace KianaBH.GameServer.Command;
|
||||||
|
|
||||||
|
public interface ICommands;
|
||||||
126
GameServer/Command/CommandManager.cs
Normal file
126
GameServer/Command/CommandManager.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using KianaBH.Database.Account;
|
||||||
|
using KianaBH.Enums.Player;
|
||||||
|
using KianaBH.GameServer.Server;
|
||||||
|
using KianaBH.Internationalization;
|
||||||
|
using KianaBH.KcpSharp;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Command;
|
||||||
|
|
||||||
|
public class CommandManager
|
||||||
|
{
|
||||||
|
public static Logger Logger { get; } = new("CommandManager");
|
||||||
|
|
||||||
|
public static Dictionary<string, ICommands> Commands { get; } = [];
|
||||||
|
public static Dictionary<string, CommandInfoAttribute> CommandInfo { get; } = [];
|
||||||
|
public static Dictionary<string, string> CommandAlias { get; } = []; // <aliaName, fullName>
|
||||||
|
|
||||||
|
public static void RegisterCommands()
|
||||||
|
{
|
||||||
|
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
||||||
|
if (typeof(ICommands).IsAssignableFrom(type) && !type.IsAbstract)
|
||||||
|
RegisterCommand(type);
|
||||||
|
|
||||||
|
Logger.Info(I18NManager.Translate("Server.ServerInfo.RegisterItem", Commands.Count.ToString(),
|
||||||
|
I18NManager.Translate("Word.Command")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterCommand(Type type)
|
||||||
|
{
|
||||||
|
var attr = type.GetCustomAttribute<CommandInfoAttribute>();
|
||||||
|
if (attr == null) return;
|
||||||
|
var instance = Activator.CreateInstance(type);
|
||||||
|
if (instance is not ICommands command) return;
|
||||||
|
Commands.Add(attr.Name, command);
|
||||||
|
CommandInfo.Add(attr.Name, attr);
|
||||||
|
|
||||||
|
// register alias
|
||||||
|
foreach (var alias in attr.Alias) // add alias
|
||||||
|
CommandAlias.Add(alias, attr.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async void HandleCommand(string input, ICommandSender sender)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var argInfo = new CommandArg(input, sender);
|
||||||
|
var target = sender.GetSender();
|
||||||
|
|
||||||
|
foreach (var arg in argInfo.Args.ToList()) // Copy
|
||||||
|
{
|
||||||
|
switch (arg[0])
|
||||||
|
{
|
||||||
|
case '-':
|
||||||
|
argInfo.Attributes.Add(arg[1..]);
|
||||||
|
break;
|
||||||
|
case '@':
|
||||||
|
_ = int.TryParse(arg[1..], out target);
|
||||||
|
argInfo.Args.Remove(arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argInfo.TargetUid = target;
|
||||||
|
if (KcpListener.Connections.Values.ToList().Find(item =>
|
||||||
|
(item as Connection)?.Player?.Uid == target) is Connection con)
|
||||||
|
argInfo.Target = con;
|
||||||
|
|
||||||
|
// find register cmd
|
||||||
|
var cmdName = argInfo.Args[0];
|
||||||
|
if (CommandAlias.TryGetValue(cmdName, out var fullName)) cmdName = fullName;
|
||||||
|
if (!Commands.TryGetValue(cmdName, out var command))
|
||||||
|
{
|
||||||
|
await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.CommandNotFound"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
argInfo.Args.RemoveAt(0);
|
||||||
|
var cmdInfo = CommandInfo[cmdName];
|
||||||
|
|
||||||
|
// Check cmd perms
|
||||||
|
if (!AccountData.HasPerm(cmdInfo.Perm, sender.GetSender()))
|
||||||
|
{
|
||||||
|
await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.NoPermission"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argInfo.Target?.Player?.Uid != sender.GetSender() && !AccountData.HasPerm([PermEnum.Other], sender.GetSender()))
|
||||||
|
{
|
||||||
|
await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.NoPermission"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find CommandMethodAttribute
|
||||||
|
var isFound = false;
|
||||||
|
foreach (var methodInfo in command.GetType().GetMethods())
|
||||||
|
{
|
||||||
|
var attr = methodInfo.GetCustomAttribute<CommandMethodAttribute>();
|
||||||
|
if (attr == null) continue;
|
||||||
|
if (argInfo.Args.Count > 0 && attr.MethodName == argInfo.Args[0])
|
||||||
|
{
|
||||||
|
argInfo.Args.RemoveAt(0);
|
||||||
|
isFound = true;
|
||||||
|
methodInfo.Invoke(command, [argInfo]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFound) return;
|
||||||
|
|
||||||
|
// find CommandDefaultAttribute
|
||||||
|
foreach (var methodInfo in command.GetType().GetMethods())
|
||||||
|
{
|
||||||
|
var attr = methodInfo.GetCustomAttribute<CommandDefaultAttribute>();
|
||||||
|
if (attr == null) continue;
|
||||||
|
isFound = true;
|
||||||
|
methodInfo.Invoke(command, [argInfo]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isFound) return;
|
||||||
|
|
||||||
|
// failed to find method
|
||||||
|
await sender.SendMsg(I18NManager.Translate(cmdInfo.Usage));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error(I18NManager.Translate("Game.Command.Notice.InternalError", ex.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
GameServer/Command/CommandSender.cs
Normal file
41
GameServer/Command/CommandSender.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using KianaBH.Enums.Player;
|
||||||
|
using KianaBH.GameServer.Game.Player;
|
||||||
|
using KianaBH.Util;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Command;
|
||||||
|
|
||||||
|
public interface ICommandSender
|
||||||
|
{
|
||||||
|
public ValueTask SendMsg(string msg);
|
||||||
|
|
||||||
|
public int GetSender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConsoleCommandSender(Logger logger) : ICommandSender
|
||||||
|
{
|
||||||
|
public async ValueTask SendMsg(string msg)
|
||||||
|
{
|
||||||
|
logger.Info(msg);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSender()
|
||||||
|
{
|
||||||
|
return (int)ServerEnum.Console;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlayerCommandSender(PlayerInstance player) : ICommandSender
|
||||||
|
{
|
||||||
|
public PlayerInstance Player = player;
|
||||||
|
|
||||||
|
public async ValueTask SendMsg(string msg)
|
||||||
|
{
|
||||||
|
// TODO SEND MSG
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSender()
|
||||||
|
{
|
||||||
|
return Player.Uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
GameServer/Game/Avatar/AvatarManager.cs
Normal file
63
GameServer/Game/Avatar/AvatarManager.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using KianaBH.Data;
|
||||||
|
using KianaBH.Data.Excel;
|
||||||
|
using KianaBH.Database;
|
||||||
|
using KianaBH.Database.Avatar;
|
||||||
|
using KianaBH.Enums.Item;
|
||||||
|
using KianaBH.GameServer.Game.Player;
|
||||||
|
using KianaBH.GameServer.Server.Packet.Send.Avatar;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Game.Avatar;
|
||||||
|
|
||||||
|
public class AvatarManager(PlayerInstance player) : BasePlayerManager(player)
|
||||||
|
{
|
||||||
|
public AvatarData AvatarData { get; } = DatabaseHelper.GetInstanceOrCreateNew<AvatarData>(player.Uid);
|
||||||
|
public async ValueTask<AvatarDataExcel?> AddAvatar(int avatarId, bool sync = true)
|
||||||
|
{
|
||||||
|
GameData.AvatarData.TryGetValue(avatarId, out var avatarExcel);
|
||||||
|
if (avatarExcel == null) return null;
|
||||||
|
|
||||||
|
var avatar = new AvatarInfo
|
||||||
|
{
|
||||||
|
Level = 1,
|
||||||
|
Timestamp = Extensions.GetUnixSec(),
|
||||||
|
Star = avatarExcel.UnlockStar,
|
||||||
|
DressId = avatarExcel.DefaultDressId,
|
||||||
|
DressList = {avatarExcel.DefaultDressId},
|
||||||
|
AvatarId = avatarExcel.AvatarID,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var skill in avatarExcel.SkillList)
|
||||||
|
{
|
||||||
|
avatar.SkillList.Add(new AvatarSkill
|
||||||
|
{
|
||||||
|
SkillId = skill
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var weapon = GameData.WeaponData.TryGetValue(avatarExcel.InitialWeapon, out var weaponConfig);
|
||||||
|
if (weaponConfig != null)
|
||||||
|
{
|
||||||
|
var item = await Player.InventoryManager!.AddItem(avatarExcel.InitialWeapon, 1, ItemMainTypeEnum.Weapon, weaponConfig.MaxLv, avatarId);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
avatar.WeaponUniqueId = item!.UniqueId;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AvatarData.Avatars.Add(avatar);
|
||||||
|
|
||||||
|
if (sync) await Player.SendPacket(new PacketGetAvatarDataRsp(new List<AvatarInfo> { avatar }, false));
|
||||||
|
|
||||||
|
return avatarExcel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvatarInfo? GetAvatar(uint avatarId)
|
||||||
|
{
|
||||||
|
|
||||||
|
return AvatarData.Avatars.Find(avatar => avatar.AvatarId == avatarId);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
GameServer/Game/BasePlayerManager.cs
Normal file
8
GameServer/Game/BasePlayerManager.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using KianaBH.GameServer.Game.Player;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Game;
|
||||||
|
|
||||||
|
public class BasePlayerManager(PlayerInstance player)
|
||||||
|
{
|
||||||
|
public PlayerInstance Player { get; private set; } = player;
|
||||||
|
}
|
||||||
86
GameServer/Game/Inventory/InventoryManager.cs
Normal file
86
GameServer/Game/Inventory/InventoryManager.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using KianaBH.Data;
|
||||||
|
using KianaBH.Database;
|
||||||
|
using KianaBH.Database.Inventory;
|
||||||
|
using KianaBH.Enums.Item;
|
||||||
|
using KianaBH.GameServer.Game.Player;
|
||||||
|
using KianaBH.Proto;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using static KianaBH.Proto.MasterPupilRetcode.Types;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Game.Inventory;
|
||||||
|
|
||||||
|
public class InventoryManager(PlayerInstance player) : BasePlayerManager(player)
|
||||||
|
{
|
||||||
|
public InventoryData Data = DatabaseHelper.GetInstanceOrCreateNew<InventoryData>(player.Uid);
|
||||||
|
|
||||||
|
public async ValueTask<ItemData?> AddItem(int itemId, int count, ItemMainTypeEnum type, int level = 1, int equipAvatar = 0, bool notify = true,
|
||||||
|
bool sync = true)
|
||||||
|
{
|
||||||
|
ItemData? itemData = null;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ItemMainTypeEnum.Material:
|
||||||
|
GameData.MaterialData.TryGetValue(itemId, out var materialConfig);
|
||||||
|
if (materialConfig == null) return null;
|
||||||
|
itemData = await PutItem(itemId, count, type);
|
||||||
|
break;
|
||||||
|
case ItemMainTypeEnum.Weapon:
|
||||||
|
GameData.WeaponData.TryGetValue(itemId, out var weaponConfig);
|
||||||
|
if (weaponConfig == null) return null;
|
||||||
|
itemData = await PutItem(itemId, 1, type, level, equipAvatar: equipAvatar, uniqueId: ++Data.NextUniqueId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<ItemData> PutItem(int itemId, int count, ItemMainTypeEnum type, int level = 0,
|
||||||
|
int exp = 0, int equipAvatar = 0, int uniqueId = 0)
|
||||||
|
{
|
||||||
|
var item = new ItemData
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
Count = count,
|
||||||
|
Level = level,
|
||||||
|
Exp = exp,
|
||||||
|
EquipAvatar = equipAvatar,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (uniqueId > 0) item.UniqueId = uniqueId;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ItemMainTypeEnum.Material:
|
||||||
|
var oldItem = Data.MaterialItems.Find(x => x.ItemId == itemId);
|
||||||
|
if (oldItem != null)
|
||||||
|
{
|
||||||
|
oldItem.Count += count;
|
||||||
|
item = oldItem;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.MaterialItems.Add(item);
|
||||||
|
break;
|
||||||
|
case ItemMainTypeEnum.Weapon:
|
||||||
|
if (Data.WeaponItems.Count + 1 > GameConstants.INVENTORY_MAX_EQUIPMENT)
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.WeaponItems.Add(item);
|
||||||
|
break;
|
||||||
|
case ItemMainTypeEnum.Stigmata:
|
||||||
|
if (Data.StigmataItems.Count + 1 > GameConstants.INVENTORY_MAX_EQUIPMENT)
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
Data.StigmataItems.Add(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
110
GameServer/Game/Player/PlayerInstance.cs
Normal file
110
GameServer/Game/Player/PlayerInstance.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using KianaBH.Data;
|
||||||
|
using KianaBH.Database;
|
||||||
|
using KianaBH.Database.Account;
|
||||||
|
using KianaBH.Database.Client;
|
||||||
|
using KianaBH.Database.Player;
|
||||||
|
using KianaBH.GameServer.Game.Avatar;
|
||||||
|
using KianaBH.GameServer.Game.Inventory;
|
||||||
|
using KianaBH.GameServer.Server;
|
||||||
|
using KianaBH.KcpSharp;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Game.Player;
|
||||||
|
|
||||||
|
public class PlayerInstance(PlayerData data)
|
||||||
|
{
|
||||||
|
public AvatarManager? AvatarManager { get; private set; }
|
||||||
|
public InventoryManager? InventoryManager { get; private set; }
|
||||||
|
|
||||||
|
public static readonly List<PlayerInstance> _playerInstances = [];
|
||||||
|
public PlayerData Data { get; set; } = data;
|
||||||
|
public ClientData? ClientData { get; private set; }
|
||||||
|
public GuideData? GuideData { get; private set; }
|
||||||
|
public int Uid { get; set; }
|
||||||
|
public Connection? Connection { get; set; }
|
||||||
|
public bool Initialized { get; set; }
|
||||||
|
public bool IsNewPlayer { get; set; }
|
||||||
|
public int GetMissionDataRequestCount = 0;
|
||||||
|
|
||||||
|
#region Initializers
|
||||||
|
public PlayerInstance(int uid) : this(new PlayerData { Uid = uid })
|
||||||
|
{
|
||||||
|
// new player
|
||||||
|
IsNewPlayer = true;
|
||||||
|
Data.Name = AccountData.GetAccountByUid(uid)?.Username;
|
||||||
|
|
||||||
|
DatabaseHelper.CreateInstance(Data);
|
||||||
|
|
||||||
|
var t = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await InitialPlayerManager();
|
||||||
|
await AvatarManager!.AddAvatar(101);
|
||||||
|
GuideData?.GuideFinishList.AddRange(GameData.TutorialData.Values.Select(x => x.Id));
|
||||||
|
});
|
||||||
|
t.Wait();
|
||||||
|
|
||||||
|
Initialized = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
private async ValueTask InitialPlayerManager()
|
||||||
|
{
|
||||||
|
Uid = Data.Uid;
|
||||||
|
AvatarManager = new AvatarManager(this);
|
||||||
|
InventoryManager = new InventoryManager(this);
|
||||||
|
ClientData = InitializeDatabase<ClientData>();
|
||||||
|
GuideData = InitializeDatabase<GuideData>();
|
||||||
|
Data.LastActiveTime = Extensions.GetUnixSec();
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
public T InitializeDatabase<T>() where T : BaseDatabaseDataHelper, new()
|
||||||
|
{
|
||||||
|
var instance = DatabaseHelper.GetInstanceOrCreateNew<T>(Uid);
|
||||||
|
return instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Network
|
||||||
|
public async ValueTask OnGetToken()
|
||||||
|
{
|
||||||
|
if (!Initialized) await InitialPlayerManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask OnLogin()
|
||||||
|
{
|
||||||
|
_playerInstances.Add(this);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PlayerInstance? GetPlayerInstanceByUid(long uid)
|
||||||
|
=> _playerInstances.FirstOrDefault(player => player.Uid == uid);
|
||||||
|
public void OnLogoutAsync()
|
||||||
|
{
|
||||||
|
_playerInstances.Remove(this);
|
||||||
|
}
|
||||||
|
public async ValueTask SendPacket(BasePacket packet)
|
||||||
|
{
|
||||||
|
if (Connection?.IsOnline == true) await Connection.SendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Actions
|
||||||
|
public async ValueTask OnHeartBeat()
|
||||||
|
{
|
||||||
|
DatabaseHelper.ToSaveUidList.SafeAdd(Uid);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Serialization
|
||||||
|
|
||||||
|
public Proto.GetMainDataRsp ToProto()
|
||||||
|
{
|
||||||
|
return Data.ToProto();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
22
GameServer/GameServer.csproj
Normal file
22
GameServer/GameServer.csproj
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<CETCompat>false</CETCompat>
|
||||||
|
<RootNamespace>KianaBH.GameServer</RootNamespace>
|
||||||
|
<StartupObject></StartupObject>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<AssemblyName>KianaGameServer</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
<ProjectReference Include="..\KcpSharp\KcpSharp.csproj" />
|
||||||
|
<ProjectReference Include="..\Proto\Proto.csproj" />
|
||||||
|
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
189
GameServer/Server/Connection.cs
Normal file
189
GameServer/Server/Connection.cs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
using KianaBH.GameServer.Game.Player;
|
||||||
|
using KianaBH.GameServer.Server.Packet;
|
||||||
|
using KianaBH.KcpSharp;
|
||||||
|
using KianaBH.KcpSharp.Base;
|
||||||
|
using KianaBH.Proto;
|
||||||
|
using KianaBH.Util;
|
||||||
|
using KianaBH.Util.Extensions;
|
||||||
|
using KianaBH.Util.Security;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using Google.Protobuf.Reflection;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Reflection.PortableExecutable;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
namespace KianaBH.GameServer.Server;
|
||||||
|
|
||||||
|
public class Connection(KcpConversation conversation, IPEndPoint remote) : KcpConnection(conversation, remote)
|
||||||
|
{
|
||||||
|
private static readonly Logger Logger = new("GameServer");
|
||||||
|
|
||||||
|
public PlayerInstance? Player { get; set; }
|
||||||
|
|
||||||
|
private static readonly HashSet<string> DummyPacketNames =
|
||||||
|
[
|
||||||
|
"AddGoodfeelReq", "ArkPlusActivityGetDataReq", "BuffAssistGetActivityReq", "BwWorldCampActivityGetDataReq",
|
||||||
|
"ChatworldBeastGetActivityReq", "ChatworldGetPrayInfoReq", "ClientReportReq", "GetAdventureGroupReq",
|
||||||
|
"GetArmadaDataReq", "GetArmadaStageScoreActivityReq", "GetAskAddFriendListReq", "GetAssistantFrozenListReq",
|
||||||
|
"GetAvatarMissionActivityReq", "GetBattlePassMissionPanelReq", "GetBlackListReq", "GetCardProductInfoReq",
|
||||||
|
"GetChapterActivityDataReq", "GetChapterCompensationInfoReq", "GetChatgroupListReq", "GetClientMailDataReq",
|
||||||
|
"GetConsignedOrderDataReq", "GetCurrencyExchangeInfoReq", "GetExtractReforgeActivityReq",
|
||||||
|
"GetFarmActivityDataReq", "GetFriendListReq", "GetFriendRemarkListReq", "GetGachaDisplayReq",
|
||||||
|
"GetGardenScheduleReq", "GetGobackReq", "GetGratuityActivityReq", "GetMasterPupilCardReq",
|
||||||
|
"GetMasterPupilDataReq", "GetMasterPupilMainDataReq", "GetMosaicActivityReq", "GetNewbieActivityReq",
|
||||||
|
"GetNinjaActivityReq", "GetOfflineResourceDataReq", "GetOpenworldQuestActivityReq",
|
||||||
|
"GetRaffleActivityReq", "GetRankScheduleDataReq", "GetRecommendFriendListReq", "GetRecommendGoodsReq",
|
||||||
|
"GetRoomDataReq", "GetRpgTaleReq", "GetScratchTicketReq", "GetSecurityPasswordReq", "GetShoppingMallListReq",
|
||||||
|
"GetStageChapterReq", "GetSupportActivityReq", "GetSurveyDataReq",
|
||||||
|
"GetTradingCardActivityReq", "GetTvtActivityReq", "GetWeeklyRoutineActivityReq", "GrandKeyActivateSkillReq",
|
||||||
|
"MassiveWarGetActivityReq", "OpenworldGetMechaTeamReq", "OpenworldHuntActivityGetDataReq",
|
||||||
|
"PjmsGetAchievementDataReq", "PjmsGetConditionDataReq", "PjmsGetCurWorldReq", "PjmsGetStoryDataReq",
|
||||||
|
"ReunionCookGetActivityReq", "SimplifiedGodWarGetActivityReq",
|
||||||
|
"StageInnerDataReportReq", "SusannaTrialGetActivityReq", "ThemeWantedRefreshTicketReq",
|
||||||
|
"UpdateMissionProgressReq", "WaveRushGetActivityReq"
|
||||||
|
];
|
||||||
|
|
||||||
|
public override async void Start()
|
||||||
|
{
|
||||||
|
Logger.Info($"New connection from {RemoteEndPoint}.");
|
||||||
|
State = SessionStateEnum.WAITING_FOR_TOKEN;
|
||||||
|
await ReceiveLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async void Stop(bool isServerStop = false)
|
||||||
|
{
|
||||||
|
//if (isServerStop) await Player!.SendPacket(new PacketPlayerKickOutScNotify(KickType.KickLoginWhiteTimeout));
|
||||||
|
Player?.OnLogoutAsync();
|
||||||
|
KcpListener.UnregisterConnection(this);
|
||||||
|
base.Stop(isServerStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task ReceiveLoop()
|
||||||
|
{
|
||||||
|
while (!CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// WaitToReceiveAsync call completes when there is at least one message is received or the transport is closed.
|
||||||
|
var result = await Conversation.WaitToReceiveAsync(CancelToken.Token);
|
||||||
|
if (result.TransportClosed)
|
||||||
|
{
|
||||||
|
Logger.Debug("Connection was closed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = ArrayPool<byte>.Shared.Rent(result.BytesReceived);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TryReceive should not return false here, unless the transport is closed.
|
||||||
|
// So we don't need to check for result.TransportClosed.
|
||||||
|
if (!Conversation.TryReceive(buffer, out result))
|
||||||
|
{
|
||||||
|
Logger.Error("Failed to receive packet");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ProcessMessageAsync(buffer.AsMemory(0, result.BytesReceived));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error("Packet parse error", ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO THE PROCESSING OF THE GAME PACKET
|
||||||
|
private async Task ProcessMessageAsync(Memory<byte> data)
|
||||||
|
{
|
||||||
|
var gamePacket = data.ToArray();
|
||||||
|
|
||||||
|
await using MemoryStream ms = new(gamePacket);
|
||||||
|
using BinaryReader br = new(ms);
|
||||||
|
|
||||||
|
// Handle
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (br.BaseStream.Position < br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
// Length
|
||||||
|
if (br.BaseStream.Length - br.BaseStream.Position < 32) return;
|
||||||
|
// Packet sanity check
|
||||||
|
var headMagic = br.ReadUInt32BE();
|
||||||
|
if (headMagic != 0x01234567)
|
||||||
|
{
|
||||||
|
Logger.Error($"Bad Data Package Received: got 0x{headMagic:X}, expect 0x01234567");
|
||||||
|
return; // Bad packet
|
||||||
|
}
|
||||||
|
|
||||||
|
var packetVersion = br.ReadUInt16BE();
|
||||||
|
var ClientVersion = br.ReadUInt16BE();
|
||||||
|
var PacketId = br.ReadUInt32BE();
|
||||||
|
var UserId = br.ReadUInt32BE();
|
||||||
|
var UserIp = br.ReadUInt32BE();
|
||||||
|
var Sign = br.ReadUInt32BE();
|
||||||
|
var SignType = br.ReadUInt16BE();
|
||||||
|
var CmdId = br.ReadUInt16BE();
|
||||||
|
var HeaderLength = br.ReadUInt16BE();
|
||||||
|
var BodyLength = br.ReadUInt32BE();
|
||||||
|
|
||||||
|
// Data
|
||||||
|
var header = br.ReadBytes(HeaderLength);
|
||||||
|
var Body = br.ReadBytes((int)BodyLength);
|
||||||
|
var TailMagic = br.ReadUInt32BE();
|
||||||
|
LogPacket("Recv", CmdId, Body);
|
||||||
|
await HandlePacket(CmdId, header, Body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error(e.Message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandlePacket(ushort opcode, byte[] header, byte[] payload)
|
||||||
|
{
|
||||||
|
var packetName = LogMap.GetValueOrDefault(opcode);
|
||||||
|
if (DummyPacketNames.Contains(packetName!))
|
||||||
|
{
|
||||||
|
await SendDummy(packetName!);
|
||||||
|
Logger.Info($"[Dummy] Send Dummy {packetName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the Handler for this opcode
|
||||||
|
var handler = HandlerManager.GetHandler(opcode);
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
// Handle
|
||||||
|
// Make sure session is ready for packets
|
||||||
|
var state = State;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await handler.OnHandle(this, header, payload);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error(e.Message, e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendDummy(string packetName)
|
||||||
|
{
|
||||||
|
var respName = packetName.Replace("Req", "Rsp"); // Get the response packet name
|
||||||
|
if (respName == packetName) return; // do not send rsp when resp name = recv name
|
||||||
|
var respOpcode = LogMap.FirstOrDefault(x => x.Value == respName).Key; // Get the response opcode
|
||||||
|
|
||||||
|
// Send Rsp
|
||||||
|
await SendPacket(respOpcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user