Showing preview only (1,004K chars total). Download the full file or copy to clipboard to get everything.
Repository: egametang/ET
Branch: release9.0
Commit: 9bcbf5d184f5
Files: 304
Total size: 856.8 KB
Directory structure:
gitextract_fkscp4eu/
├── .gitattributes
├── .gitignore
├── Assets/
│ ├── .gitignore
│ ├── DefaultVolumeProfile.asset
│ ├── DefaultVolumeProfile.asset.meta
│ ├── Resources/
│ │ ├── BuildinFileManifest.asset
│ │ └── BuildinFileManifest.asset.meta
│ ├── Resources.meta
│ ├── Settings/
│ │ ├── Build Profiles/
│ │ │ ├── Mac.asset
│ │ │ └── Mac.asset.meta
│ │ └── Build Profiles.meta
│ ├── Settings.meta
│ ├── link.xml
│ └── link.xml.meta
├── Book/
│ ├── 1.1运行指南.md
│ ├── 1.2Why use .net core.md
│ ├── 1.2为什么使用.net core.md
│ ├── 2.1CSharp Coroutine.md
│ ├── 2.1CSharp的协程.md
│ ├── 2.2Better Coroutine.md
│ ├── 2.2更好的协程.md
│ ├── 2.3Single-threaded asynchronous.md
│ ├── 2.3单线程异步.md
│ ├── 3.2The powerful MongoBson library.md
│ ├── 3.2强大的MongoBson库.md
│ ├── 3.3Everything is Entity.md
│ ├── 3.3一切皆实体.md
│ ├── 3.4EventSystem.md
│ ├── 3.4事件机制EventSystem.md
│ ├── 4.1Component-based design.md
│ ├── 4.1组件式设计.md
│ ├── 5.4Actor Model.md
│ ├── 5.4Actor模型.md
│ ├── 5.5Actor Location-EN.md
│ ├── 5.5Actor Location-ZH.md
│ ├── 5.6Numerical component design.md
│ ├── 5.6数值组件设计.md
│ ├── 6.1AI Framwork.md
│ ├── 6.1AI框架.md
│ ├── 6.2AI框架-行为机.md
│ ├── 7.1代码规范.md
│ ├── 8.1ET Package制作指南.md
│ ├── 8.2ET Package目录.md
│ └── 8.3ET9项目怎么进行包更新.md
├── ChangeLog.md
├── Directory.Build.props
├── ET.sln.DotSettings
├── LICENSE
├── Packages/
│ ├── .gitignore
│ ├── com.etetet.init/
│ │ ├── .gitignore
│ │ ├── Editor/
│ │ │ ├── ET.Init.Editor.asmdef
│ │ │ ├── ET.Init.Editor.asmdef.meta
│ │ │ ├── GitDependencyResolver/
│ │ │ │ ├── DependencyResolver.cs
│ │ │ │ └── DependencyResolver.cs.meta
│ │ │ ├── GitDependencyResolver.meta
│ │ │ ├── PackageGit.cs
│ │ │ ├── PackageGit.cs.meta
│ │ │ ├── ProcessHelper.cs
│ │ │ └── ProcessHelper.cs.meta
│ │ ├── Editor.meta
│ │ ├── MoveToPackages.ps1
│ │ ├── MoveToPackages.ps1.meta
│ │ ├── MoveToPackages_6.ps1
│ │ ├── MoveToPackages_6.ps1.meta
│ │ ├── Plugins/
│ │ │ └── MongoDB.meta
│ │ ├── Plugins.meta
│ │ ├── package.json
│ │ └── package.json.meta
│ ├── com.halodi.halodi-unity-package-registry-manager/
│ │ ├── CHANGELOG.md
│ │ ├── CHANGELOG.md.meta
│ │ ├── Editor/
│ │ │ ├── Halodi/
│ │ │ │ ├── PackageRegistry/
│ │ │ │ │ ├── Core/
│ │ │ │ │ │ ├── CredentialManager.cs
│ │ │ │ │ │ ├── CredentialManager.cs.meta
│ │ │ │ │ │ ├── RegistryManager.cs
│ │ │ │ │ │ ├── RegistryManager.cs.meta
│ │ │ │ │ │ ├── ScopedRegistry.cs
│ │ │ │ │ │ ├── ScopedRegistry.cs.meta
│ │ │ │ │ │ ├── UpgradePackagesManager.cs
│ │ │ │ │ │ └── UpgradePackagesManager.cs.meta
│ │ │ │ │ ├── Core.meta
│ │ │ │ │ ├── NPM/
│ │ │ │ │ │ ├── NPMLogin.cs
│ │ │ │ │ │ ├── NPMLogin.cs.meta
│ │ │ │ │ │ ├── NPMPublish.cs
│ │ │ │ │ │ ├── NPMPublish.cs.meta
│ │ │ │ │ │ ├── NPMResponse.cs
│ │ │ │ │ │ ├── NPMResponse.cs.meta
│ │ │ │ │ │ ├── PackageTarball.cs
│ │ │ │ │ │ ├── PackageTarball.cs.meta
│ │ │ │ │ │ ├── PublicationManifest.cs
│ │ │ │ │ │ ├── PublicationManifest.cs.meta
│ │ │ │ │ │ ├── WebExceptionParser.cs
│ │ │ │ │ │ └── WebExceptionParser.cs.meta
│ │ │ │ │ ├── NPM.meta
│ │ │ │ │ ├── UI/
│ │ │ │ │ │ ├── BulkAddPackages.cs
│ │ │ │ │ │ ├── BulkAddPackages.cs.meta
│ │ │ │ │ │ ├── CredentialEditorView.cs
│ │ │ │ │ │ ├── CredentialEditorView.cs.meta
│ │ │ │ │ │ ├── CredentialManagerView.cs
│ │ │ │ │ │ ├── CredentialManagerView.cs.meta
│ │ │ │ │ │ ├── GetTokenView.cs
│ │ │ │ │ │ ├── GetTokenView.cs.meta
│ │ │ │ │ │ ├── InstallPackageCreator.cs
│ │ │ │ │ │ ├── InstallPackageCreator.cs.meta
│ │ │ │ │ │ ├── RegistryManagerView.cs
│ │ │ │ │ │ ├── RegistryManagerView.cs.meta
│ │ │ │ │ │ ├── ScopedRegistryEditorView.cs
│ │ │ │ │ │ ├── ScopedRegistryEditorView.cs.meta
│ │ │ │ │ │ ├── SettingsProvider.cs
│ │ │ │ │ │ ├── SettingsProvider.cs.meta
│ │ │ │ │ │ ├── UpgradePackagesView.cs
│ │ │ │ │ │ └── UpgradePackagesView.cs.meta
│ │ │ │ │ └── UI.meta
│ │ │ │ └── PackageRegistry.meta
│ │ │ ├── Halodi.PackageRegistryManager.Editor.asmdef
│ │ │ ├── Halodi.PackageRegistryManager.Editor.asmdef.meta
│ │ │ ├── Halodi.meta
│ │ │ ├── ThirdParty/
│ │ │ │ ├── LICENSE.Tomlyn.md
│ │ │ │ ├── LICENSE.Tomlyn.md.meta
│ │ │ │ ├── README.md
│ │ │ │ ├── README.md.meta
│ │ │ │ ├── Tomlyn.dll.meta
│ │ │ │ ├── Unity-SemVer/
│ │ │ │ │ ├── Artees.UnitySemVer.asmdef
│ │ │ │ │ ├── Artees.UnitySemVer.asmdef.meta
│ │ │ │ │ ├── CloudBuildManifest.cs
│ │ │ │ │ ├── CloudBuildManifest.cs.meta
│ │ │ │ │ ├── LICENSE
│ │ │ │ │ ├── LICENSE.meta
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── README.md.meta
│ │ │ │ │ ├── SemVer.cs
│ │ │ │ │ ├── SemVer.cs.meta
│ │ │ │ │ ├── SemVerAttribute.cs
│ │ │ │ │ ├── SemVerAttribute.cs.meta
│ │ │ │ │ ├── SemVerAutoBuild.cs
│ │ │ │ │ ├── SemVerAutoBuild.cs.meta
│ │ │ │ │ ├── SemVerComparer.cs
│ │ │ │ │ ├── SemVerComparer.cs.meta
│ │ │ │ │ ├── SemVerConverter.cs
│ │ │ │ │ ├── SemVerConverter.cs.meta
│ │ │ │ │ ├── SemVerErrorMessage.cs
│ │ │ │ │ ├── SemVerErrorMessage.cs.meta
│ │ │ │ │ ├── SemVerValidationResult.cs
│ │ │ │ │ ├── SemVerValidationResult.cs.meta
│ │ │ │ │ ├── SemVerValidator.cs
│ │ │ │ │ └── SemVerValidator.cs.meta
│ │ │ │ └── Unity-SemVer.meta
│ │ │ └── ThirdParty.meta
│ │ ├── Editor.meta
│ │ ├── LICENSE.md
│ │ ├── LICENSE.md.meta
│ │ ├── README.md
│ │ ├── README.md.meta
│ │ ├── Tests/
│ │ │ ├── Editor/
│ │ │ │ ├── AssemblyInfo.cs
│ │ │ │ ├── AssemblyInfo.cs.meta
│ │ │ │ ├── Halodi.PackageRegistryManager.Tests.asmdef
│ │ │ │ └── Halodi.PackageRegistryManager.Tests.asmdef.meta
│ │ │ └── Editor.meta
│ │ ├── Tests.meta
│ │ ├── package.json
│ │ └── package.json.meta
│ ├── com.unity.ide.rider/
│ │ ├── .editorconfig
│ │ ├── .signature
│ │ ├── CHANGELOG.md
│ │ ├── CHANGELOG.md.meta
│ │ ├── CONTRIBUTING.md
│ │ ├── CONTRIBUTING.md.meta
│ │ ├── Documentation~/
│ │ │ ├── README.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── index.md
│ │ │ └── using-the-jetbrains-rider-editor-package.md
│ │ ├── LICENSE.md
│ │ ├── LICENSE.md.meta
│ │ ├── Rider/
│ │ │ ├── Editor/
│ │ │ │ ├── Discovery.cs
│ │ │ │ ├── Discovery.cs.meta
│ │ │ │ ├── EditorPluginInterop.cs
│ │ │ │ ├── EditorPluginInterop.cs.meta
│ │ │ │ ├── JetBrains.Rider.PathLocator.dll.meta
│ │ │ │ ├── LoggingLevel.cs
│ │ │ │ ├── LoggingLevel.cs.meta
│ │ │ │ ├── PluginSettings.cs
│ │ │ │ ├── PluginSettings.cs.meta
│ │ │ │ ├── PostProcessors/
│ │ │ │ │ ├── RiderAssetPostprocessor.cs
│ │ │ │ │ └── RiderAssetPostprocessor.cs.meta
│ │ │ │ ├── PostProcessors.meta
│ │ │ │ ├── ProjectGeneration/
│ │ │ │ │ ├── AssemblyNameProvider.cs
│ │ │ │ │ ├── AssemblyNameProvider.cs.meta
│ │ │ │ │ ├── FileIOProvider.cs
│ │ │ │ │ ├── FileIOProvider.cs.meta
│ │ │ │ │ ├── GUIDProvider.cs
│ │ │ │ │ ├── GUIDProvider.cs.meta
│ │ │ │ │ ├── IAssemblyNameProvider.cs
│ │ │ │ │ ├── IAssemblyNameProvider.cs.meta
│ │ │ │ │ ├── IFileIO.cs
│ │ │ │ │ ├── IFileIO.cs.meta
│ │ │ │ │ ├── IGUIDGenerator.cs
│ │ │ │ │ ├── IGUIDGenerator.cs.meta
│ │ │ │ │ ├── IGenerator.cs
│ │ │ │ │ ├── IGenerator.cs.meta
│ │ │ │ │ ├── LastWriteTracker.cs
│ │ │ │ │ ├── LastWriteTracker.cs.meta
│ │ │ │ │ ├── PackageManagerTracker.cs
│ │ │ │ │ ├── PackageManagerTracker.cs.meta
│ │ │ │ │ ├── ProjectGeneration.cs
│ │ │ │ │ ├── ProjectGeneration.cs.meta
│ │ │ │ │ ├── ProjectGenerationFlag.cs
│ │ │ │ │ ├── ProjectGenerationFlag.cs.meta
│ │ │ │ │ ├── ProjectPart.cs
│ │ │ │ │ ├── ProjectPart.cs.meta
│ │ │ │ │ ├── SolutionGuidGenerator.cs
│ │ │ │ │ └── SolutionGuidGenerator.cs.meta
│ │ │ │ ├── ProjectGeneration.meta
│ │ │ │ ├── Properties/
│ │ │ │ │ ├── AssemblyInfo.cs
│ │ │ │ │ └── AssemblyInfo.cs.meta
│ │ │ │ ├── Properties.meta
│ │ │ │ ├── RiderInitializer.cs
│ │ │ │ ├── RiderInitializer.cs.meta
│ │ │ │ ├── RiderScriptEditor.cs
│ │ │ │ ├── RiderScriptEditor.cs.meta
│ │ │ │ ├── RiderScriptEditorData.cs
│ │ │ │ ├── RiderScriptEditorData.cs.meta
│ │ │ │ ├── RiderScriptEditorDataPersisted.cs
│ │ │ │ ├── RiderScriptEditorDataPersisted.cs.meta
│ │ │ │ ├── RiderStyles.cs
│ │ │ │ ├── RiderStyles.cs.meta
│ │ │ │ ├── StartUpMethodExecutor.cs
│ │ │ │ ├── StartUpMethodExecutor.cs.meta
│ │ │ │ ├── UnitTesting/
│ │ │ │ │ ├── CallbackData.cs
│ │ │ │ │ ├── CallbackData.cs.meta
│ │ │ │ │ ├── CallbackInitializer.cs
│ │ │ │ │ ├── CallbackInitializer.cs.meta
│ │ │ │ │ ├── RiderTestRunner.cs
│ │ │ │ │ ├── RiderTestRunner.cs.meta
│ │ │ │ │ ├── SyncTestRunCallback.cs
│ │ │ │ │ ├── SyncTestRunCallback.cs.meta
│ │ │ │ │ ├── SyncTestRunEventsHandler.cs
│ │ │ │ │ ├── SyncTestRunEventsHandler.cs.meta
│ │ │ │ │ ├── TestEvent.cs
│ │ │ │ │ ├── TestEvent.cs.meta
│ │ │ │ │ ├── TestsCallback.cs
│ │ │ │ │ └── TestsCallback.cs.meta
│ │ │ │ ├── UnitTesting.meta
│ │ │ │ ├── Util/
│ │ │ │ │ ├── CommandLineParser.cs
│ │ │ │ │ ├── CommandLineParser.cs.meta
│ │ │ │ │ ├── FileSystemUtil.cs
│ │ │ │ │ ├── FileSystemUtil.cs.meta
│ │ │ │ │ ├── LibcNativeInterop.cs
│ │ │ │ │ ├── LibcNativeInterop.cs.meta
│ │ │ │ │ ├── RiderMenu.cs
│ │ │ │ │ ├── RiderMenu.cs.meta
│ │ │ │ │ ├── RiderPathUtil.cs
│ │ │ │ │ ├── RiderPathUtil.cs.meta
│ │ │ │ │ ├── SerializableVersion.cs
│ │ │ │ │ ├── SerializableVersion.cs.meta
│ │ │ │ │ ├── StringBuilderExtensions.cs
│ │ │ │ │ ├── StringBuilderExtensions.cs.meta
│ │ │ │ │ ├── StringUtils.cs
│ │ │ │ │ └── StringUtils.cs.meta
│ │ │ │ ├── Util.meta
│ │ │ │ ├── com.unity.ide.rider.asmdef
│ │ │ │ └── com.unity.ide.rider.asmdef.meta
│ │ │ └── Editor.meta
│ │ ├── Rider.meta
│ │ ├── package.json
│ │ └── package.json.meta
│ ├── manifest.json
│ └── packages-lock.json
├── ProjectSettings/
│ ├── AudioManager.asset
│ ├── AutoStreamingSettings.asset
│ ├── BurstAotSettings_Android.json
│ ├── BurstAotSettings_StandaloneOSX.json
│ ├── BurstAotSettings_StandaloneWindows.json
│ ├── BurstAotSettings_iOS.json
│ ├── ClusterInputManager.asset
│ ├── CommonBurstAotSettings.json
│ ├── DynamicsManager.asset
│ ├── EditorBuildSettings.asset
│ ├── EditorSettings.asset
│ ├── GraphicsSettings.asset
│ ├── HybridCLRSettings.asset
│ ├── InputManager.asset
│ ├── MemorySettings.asset
│ ├── MultiplayerManager.asset
│ ├── NavMeshAreas.asset
│ ├── NavMeshLayers.asset
│ ├── NetworkManager.asset
│ ├── PackageManagerSettings.asset
│ ├── Physics2DSettings.asset
│ ├── PresetManager.asset
│ ├── ProjectSettings.asset
│ ├── ProjectVersion.txt
│ ├── QualitySettings.asset
│ ├── SceneTemplateSettings.json
│ ├── ShaderGraphSettings.asset
│ ├── TagManager.asset
│ ├── TimeManager.asset
│ ├── TimelineSettings.asset
│ ├── URPProjectSettings.asset
│ ├── UnityConnectSettings.asset
│ ├── VFXManager.asset
│ ├── VersionControlSettings.asset
│ └── XRSettings.asset
├── README.md
├── Scripts/
│ └── Publish-linux-x64.ps1
├── Unity.sln.DotSettings
└── Unity.userprefs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
#IDE文件
.vs/
.idea/
.vscode/
_ReSharper.CSharp/
#svn
*/.svn/
.svn/
#Excel的临时文件
~$*.xlsx
~$*.xlsx.meta
#Unity相关目录
.gradle
Bundles
Library
Logs
UserSettings
/*.csproj
*.vsconfig
ProjectSettings/RiderScriptEditorPersistedState.asset
HybridCLRData
#Unity/Assets
Assets/Bundles/Code/*
Assets/Bundles/AotDlls/*
Assets/StreamingAssets/
Assets/StreamingAssets.meta
#其他
.objs/
.DS_Store
/*.user
bin/
Bin/
/Log
/Logs
/**/obj/
/obj/
/Release
/Temp
/Tools/MongoDB
Publish/
*/Temp/
*/TestResults/
Assets/Generate/Model/Proto*
Assets/Generate/Model/Excel*
MongoDB/
Unity.sln
Unity.sln.DotSettings.user
/ET.sln
Assets/Plugins/Sirenix*
Assets/HybridCLR*
================================================
FILE: Assets/.gitignore
================================================
================================================
FILE: Assets/DefaultVolumeProfile.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8794448539801599142
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fb60a22f311433c4c962b888d1393f88, type: 3}
m_Name: PaniniProjection
m_EditorClassIdentifier:
active: 1
distance:
m_OverrideState: 1
m_Value: 0
cropToFit:
m_OverrideState: 1
m_Value: 1
--- !u!114 &-8291749687215229861
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 221518ef91623a7438a71fef23660601, type: 3}
m_Name: WhiteBalance
m_EditorClassIdentifier:
active: 1
temperature:
m_OverrideState: 1
m_Value: 0
tint:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-7181315038608799321
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3}
m_Name: SplitToning
m_EditorClassIdentifier:
active: 1
shadows:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
highlights:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
balance:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-6873332034207124496
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3}
m_Name: Tonemapping
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 0
neutralHDRRangeReductionMode:
m_OverrideState: 1
m_Value: 2
acesPreset:
m_OverrideState: 1
m_Value: 3
hueShiftAmount:
m_OverrideState: 1
m_Value: 0
detectPaperWhite:
m_OverrideState: 1
m_Value: 0
paperWhite:
m_OverrideState: 1
m_Value: 300
detectBrightnessLimits:
m_OverrideState: 1
m_Value: 1
minNits:
m_OverrideState: 1
m_Value: 0.005
maxNits:
m_OverrideState: 1
m_Value: 1000
--- !u!114 &-6855864280655889699
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3}
m_Name: ColorAdjustments
m_EditorClassIdentifier:
active: 1
postExposure:
m_OverrideState: 1
m_Value: 0
contrast:
m_OverrideState: 1
m_Value: 0
colorFilter:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
hueShift:
m_OverrideState: 1
m_Value: 0
saturation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-5961836973711844057
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81180773991d8724ab7f2d216912b564, type: 3}
m_Name: ChromaticAberration
m_EditorClassIdentifier:
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-4786318987195808616
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e021b4c809a781e468c2988c016ebbea, type: 3}
m_Name: ColorLookup
m_EditorClassIdentifier:
active: 1
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
contribution:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-4262818703771780653
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 29fa0085f50d5e54f8144f766051a691, type: 3}
m_Name: FilmGrain
m_EditorClassIdentifier:
active: 1
type:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
response:
m_OverrideState: 1
m_Value: 0.8
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
--- !u!114 &-3017658206072825247
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 06437c1ff663d574d9447842ba0a72e4, type: 3}
m_Name: ScreenSpaceLensFlare
m_EditorClassIdentifier:
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
tintColor:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
bloomMip:
m_OverrideState: 1
m_Value: 1
firstFlareIntensity:
m_OverrideState: 1
m_Value: 1
secondaryFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareScale:
m_OverrideState: 1
m_Value: {x: 1, y: 1}
samples:
m_OverrideState: 1
m_Value: 1
sampleDimmer:
m_OverrideState: 1
m_Value: 0.5
vignetteEffect:
m_OverrideState: 1
m_Value: 1
startingPosition:
m_OverrideState: 1
m_Value: 1.25
scale:
m_OverrideState: 1
m_Value: 1.5
streaksIntensity:
m_OverrideState: 1
m_Value: 0
streaksLength:
m_OverrideState: 1
m_Value: 0.5
streaksOrientation:
m_OverrideState: 1
m_Value: 0
streaksThreshold:
m_OverrideState: 1
m_Value: 0.25
resolution:
m_OverrideState: 1
m_Value: 4
chromaticAbberationIntensity:
m_OverrideState: 1
m_Value: 0.5
--- !u!114 &-2991397607662196306
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c01700fd266d6914ababb731e09af2eb, type: 3}
m_Name: DepthOfField
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 0
gaussianStart:
m_OverrideState: 1
m_Value: 10
gaussianEnd:
m_OverrideState: 1
m_Value: 30
gaussianMaxRadius:
m_OverrideState: 1
m_Value: 1
highQualitySampling:
m_OverrideState: 1
m_Value: 0
focusDistance:
m_OverrideState: 1
m_Value: 10
aperture:
m_OverrideState: 1
m_Value: 5.6
focalLength:
m_OverrideState: 1
m_Value: 50
bladeCount:
m_OverrideState: 1
m_Value: 5
bladeCurvature:
m_OverrideState: 1
m_Value: 1
bladeRotation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-2399972201891767753
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ccf1aba9553839d41ae37dd52e9ebcce, type: 3}
m_Name: MotionBlur
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 0
quality:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
clamp:
m_OverrideState: 1
m_Value: 0.05
--- !u!114 &-1966137642871919569
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3eb4b772797da9440885e8bd939e9560, type: 3}
m_Name: ColorCurves
m_EditorClassIdentifier:
active: 1
master:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
red:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
green:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
blue:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsHue:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
satVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
lumVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &-596686748398721985
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3}
m_Name: LiftGammaGain
m_EditorClassIdentifier:
active: 1
lift:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gamma:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gain:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
--- !u!114 &-280383500228153280
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 558a8e2b6826cf840aae193990ba9f2e, type: 3}
m_Name: ShadowsMidtonesHighlights
m_EditorClassIdentifier:
active: 1
shadows:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
midtones:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
highlights:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
shadowsStart:
m_OverrideState: 1
m_Value: 0
shadowsEnd:
m_OverrideState: 1
m_Value: 0.3
highlightsStart:
m_OverrideState: 1
m_Value: 0.55
highlightsEnd:
m_OverrideState: 1
m_Value: 1
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
m_Name: DefaultVolumeProfile
m_EditorClassIdentifier:
components:
- {fileID: -5961836973711844057}
- {fileID: -4786318987195808616}
- {fileID: -8291749687215229861}
- {fileID: -2399972201891767753}
- {fileID: -6873332034207124496}
- {fileID: -280383500228153280}
- {fileID: -2991397607662196306}
- {fileID: -6855864280655889699}
- {fileID: 2385114419025452938}
- {fileID: 5686534837009777132}
- {fileID: -596686748398721985}
- {fileID: 8045345597058296069}
- {fileID: -1966137642871919569}
- {fileID: -8794448539801599142}
- {fileID: -4262818703771780653}
- {fileID: 4746937895915073762}
- {fileID: -3017658206072825247}
- {fileID: -7181315038608799321}
- {fileID: 367049241085752711}
--- !u!114 &367049241085752711
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6bd486065ce11414fa40e631affc4900, type: 3}
m_Name: ProbeVolumesOptions
m_EditorClassIdentifier:
active: 1
normalBias:
m_OverrideState: 1
m_Value: 0.05
viewBias:
m_OverrideState: 1
m_Value: 0.1
scaleBiasWithMinProbeDistance:
m_OverrideState: 1
m_Value: 0
samplingNoise:
m_OverrideState: 1
m_Value: 0.1
animateSamplingNoise:
m_OverrideState: 1
m_Value: 1
leakReductionMode:
m_OverrideState: 1
m_Value: 2
minValidDotProductValue:
m_OverrideState: 1
m_Value: 0.1
occlusionOnlyReflectionNormalization:
m_OverrideState: 1
m_Value: 1
intensityMultiplier:
m_OverrideState: 1
m_Value: 1
skyOcclusionIntensityMultiplier:
m_OverrideState: 1
m_Value: 1
worldOffset:
m_OverrideState: 1
m_Value: {x: 0, y: 0, z: 0}
--- !u!114 &2385114419025452938
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3}
m_Name: Vignette
m_EditorClassIdentifier:
active: 1
color:
m_OverrideState: 1
m_Value: {r: 0, g: 0, b: 0, a: 1}
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
intensity:
m_OverrideState: 1
m_Value: 0
smoothness:
m_OverrideState: 1
m_Value: 0.2
rounded:
m_OverrideState: 1
m_Value: 0
--- !u!114 &4746937895915073762
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3}
m_Name: LensDistortion
m_EditorClassIdentifier:
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
xMultiplier:
m_OverrideState: 1
m_Value: 1
yMultiplier:
m_OverrideState: 1
m_Value: 1
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
scale:
m_OverrideState: 1
m_Value: 1
--- !u!114 &5686534837009777132
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cdfbdbb87d3286943a057f7791b43141, type: 3}
m_Name: ChannelMixer
m_EditorClassIdentifier:
active: 1
redOutRedIn:
m_OverrideState: 1
m_Value: 100
redOutGreenIn:
m_OverrideState: 1
m_Value: 0
redOutBlueIn:
m_OverrideState: 1
m_Value: 0
greenOutRedIn:
m_OverrideState: 1
m_Value: 0
greenOutGreenIn:
m_OverrideState: 1
m_Value: 100
greenOutBlueIn:
m_OverrideState: 1
m_Value: 0
blueOutRedIn:
m_OverrideState: 1
m_Value: 0
blueOutGreenIn:
m_OverrideState: 1
m_Value: 0
blueOutBlueIn:
m_OverrideState: 1
m_Value: 100
--- !u!114 &8045345597058296069
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
m_Name: Bloom
m_EditorClassIdentifier:
active: 1
skipIterations:
m_OverrideState: 1
m_Value: 1
threshold:
m_OverrideState: 1
m_Value: 0.9
intensity:
m_OverrideState: 1
m_Value: 0
scatter:
m_OverrideState: 1
m_Value: 0.7
clamp:
m_OverrideState: 1
m_Value: 65472
tint:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
highQualityFiltering:
m_OverrideState: 1
m_Value: 0
downscale:
m_OverrideState: 1
m_Value: 0
maxIterations:
m_OverrideState: 1
m_Value: 6
dirtTexture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
dirtIntensity:
m_OverrideState: 1
m_Value: 0
================================================
FILE: Assets/DefaultVolumeProfile.asset.meta
================================================
fileFormatVersion: 2
guid: a722862efbc47421c908e1e722eba667
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Resources/BuildinFileManifest.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 71b02dfa7aa9d4545b3417a18477fbee, type: 3}
m_Name: BuildinFileManifest
m_EditorClassIdentifier:
BuildinFiles:
- PackageName: DefaultPackage
FileName: 147968a4c6cd7a95181e4a092e5e10c6.bundle
FileCRC32: 567ffa54
- PackageName: DefaultPackage
FileName: 3425f94f739fe98d1bd89c0749aa8e11.bundle
FileCRC32: 0907b096
- PackageName: DefaultPackage
FileName: 4c3e0c50717e8bfd5834f4a8b61e3fd6.bundle
FileCRC32: c683628e
- PackageName: DefaultPackage
FileName: 56b89de42fec4292516c2328bf5aca8b.bundle
FileCRC32: 7af32f12
- PackageName: DefaultPackage
FileName: 5afeb41166a6d809ce8786e74f717023.bundle
FileCRC32: 8540f538
- PackageName: DefaultPackage
FileName: 7ac619fc67603865d284ea164d1080ff.bundle
FileCRC32: 61ec1557
- PackageName: DefaultPackage
FileName: 87d68d5fe5c55aebff3c1fe2faed2c62.bundle
FileCRC32: 0939a372
- PackageName: DefaultPackage
FileName: 954000280943584f2a8faacc0792bf58.bundle
FileCRC32: 2db2f125
- PackageName: DefaultPackage
FileName: 9dd6926894f6017b8e6cbd3da83aa6e6.bundle
FileCRC32: 9843eba6
- PackageName: DefaultPackage
FileName: a754b26c34c740e479f5a29193683718.bundle
FileCRC32: ffd1a30c
- PackageName: DefaultPackage
FileName: b2844a6487361a48b9f0b7b4ffcfe5a3.bundle
FileCRC32: c44328ce
- PackageName: DefaultPackage
FileName: bedbd2c117105ef4a813f64810402a44.bundle
FileCRC32: 75fb7c9c
- PackageName: DefaultPackage
FileName: c0e42266a4d36a5145f8dda2cad647a3.bundle
FileCRC32: a0318fcc
- PackageName: DefaultPackage
FileName: e06279c788626e8310d94d58ef235d49.bundle
FileCRC32: e1678ed4
- PackageName: DefaultPackage
FileName: e244fff3d60759e1e3f614edbfeaf7d1.bundle
FileCRC32: de496837
- PackageName: DefaultPackage
FileName: e3103bccba7a78765e30c8008337fc2a.bundle
FileCRC32: b5c8ab15
- PackageName: DefaultPackage
FileName: e505d3499c990c551faee4f4f8c90ff0.bundle
FileCRC32: 48237dd9
- PackageName: DefaultPackage
FileName: ef8675f933b496cc8967585a1d485311.bundle
FileCRC32: 6bae558c
================================================
FILE: Assets/Resources/BuildinFileManifest.asset.meta
================================================
fileFormatVersion: 2
guid: e70bfb3ccac3b4f43af72c0a8b8cdd63
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Resources.meta
================================================
fileFormatVersion: 2
guid: fe14f6ff4d78a464fbc6dd761d73e38e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Settings/Build Profiles/Mac.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
m_Name: Mac
m_EditorClassIdentifier:
m_AssetVersion: 1
m_BuildTarget: 2
m_Subtarget: 2
m_PlatformId: 0d2129357eac403d8b359c2dcbf82502
m_PlatformBuildProfile:
rid: 6418588523325555037
m_OverrideGlobalSceneList: 0
m_Scenes: []
m_ScriptingDefines: []
m_PlayerSettingsYaml:
m_Settings:
- line: '| PlayerSettings:'
- line: '| m_ObjectHideFlags: 0'
- line: '| serializedVersion: 28'
- line: '| productGUID: 1eb5b450436966b49a962ffe89a9d776'
- line: '| AndroidProfiler: 0'
- line: '| AndroidFilterTouchesWhenObscured: 0'
- line: '| AndroidEnableSustainedPerformanceMode: 0'
- line: '| defaultScreenOrientation: 4'
- line: '| targetDevice: 2'
- line: '| useOnDemandResources: 0'
- line: '| accelerometerFrequency: 60'
- line: '| companyName: test'
- line: '| productName: ET'
- line: '| defaultCursor: {instanceID: 0}'
- line: '| cursorHotspot: {x: 0, y: 0}'
- line: '| m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b:
0.1254902, a: 1}'
- line: '| m_ShowUnitySplashScreen: 1'
- line: '| m_ShowUnitySplashLogo: 1'
- line: '| m_SplashScreenOverlayOpacity: 1'
- line: '| m_SplashScreenAnimation: 1'
- line: '| m_SplashScreenLogoStyle: 1'
- line: '| m_SplashScreenDrawMode: 0'
- line: '| m_SplashScreenBackgroundAnimationZoom: 1'
- line: '| m_SplashScreenLogoAnimationZoom: 1'
- line: '| m_SplashScreenBackgroundLandscapeAspect: 1'
- line: '| m_SplashScreenBackgroundPortraitAspect: 1'
- line: '| m_SplashScreenBackgroundLandscapeUvs:'
- line: '| serializedVersion: 2'
- line: '| x: 0'
- line: '| y: 0'
- line: '| width: 1'
- line: '| height: 1'
- line: '| m_SplashScreenBackgroundPortraitUvs:'
- line: '| serializedVersion: 2'
- line: '| x: 0'
- line: '| y: 0'
- line: '| width: 1'
- line: '| height: 1'
- line: '| m_SplashScreenLogos: []'
- line: '| m_VirtualRealitySplashScreen: {instanceID: 0}'
- line: '| m_HolographicTrackingLossScreen: {instanceID: 0}'
- line: '| defaultScreenWidth: 1024'
- line: '| defaultScreenHeight: 768'
- line: '| defaultScreenWidthWeb: 960'
- line: '| defaultScreenHeightWeb: 600'
- line: '| m_StereoRenderingPath: 0'
- line: '| m_ActiveColorSpace: 1'
- line: '| unsupportedMSAAFallback: 0'
- line: '| m_SpriteBatchMaxVertexCount: 65535'
- line: '| m_SpriteBatchVertexThreshold: 300'
- line: '| m_MTRendering: 1'
- line: '| mipStripping: 0'
- line: '| numberOfMipsStripped: 0'
- line: '| numberOfMipsStrippedPerMipmapLimitGroup: {}'
- line: '| m_StackTraceTypes: 010000000100000001000000010000000100000001000000'
- line: '| iosShowActivityIndicatorOnLoading: -1'
- line: '| androidShowActivityIndicatorOnLoading: -1'
- line: '| iosUseCustomAppBackgroundBehavior: 0'
- line: '| allowedAutorotateToPortrait: 1'
- line: '| allowedAutorotateToPortraitUpsideDown: 1'
- line: '| allowedAutorotateToLandscapeRight: 1'
- line: '| allowedAutorotateToLandscapeLeft: 1'
- line: '| useOSAutorotation: 1'
- line: '| use32BitDisplayBuffer: 1'
- line: '| preserveFramebufferAlpha: 0'
- line: '| disableDepthAndStencilBuffers: 0'
- line: '| androidStartInFullscreen: 1'
- line: '| androidRenderOutsideSafeArea: 0'
- line: '| androidUseSwappy: 0'
- line: '| androidBlitType: 0'
- line: '| androidResizeableActivity: 0'
- line: '| androidDefaultWindowWidth: 1920'
- line: '| androidDefaultWindowHeight: 1080'
- line: '| androidMinimumWindowWidth: 400'
- line: '| androidMinimumWindowHeight: 300'
- line: '| androidFullscreenMode: 1'
- line: '| androidAutoRotationBehavior: 1'
- line: '| androidPredictiveBackSupport: 0'
- line: '| androidApplicationEntry: 1'
- line: '| defaultIsNativeResolution: 1'
- line: '| macRetinaSupport: 1'
- line: '| runInBackground: 1'
- line: '| muteOtherAudioSources: 0'
- line: '| Prepare IOS For Recording: 0'
- line: '| Force IOS Speakers When Recording: 0'
- line: '| deferSystemGesturesMode: 0'
- line: '| hideHomeButton: 0'
- line: '| submitAnalytics: 1'
- line: '| usePlayerLog: 1'
- line: '| dedicatedServerOptimizations: 0'
- line: '| bakeCollisionMeshes: 0'
- line: '| forceSingleInstance: 0'
- line: '| useFlipModelSwapchain: 1'
- line: '| resizableWindow: 0'
- line: '| useMacAppStoreValidation: 0'
- line: '| macAppStoreCategory: public.app-category.games'
- line: '| gpuSkinning: 0'
- line: '| meshDeformation: 0'
- line: '| xboxPIXTextureCapture: 0'
- line: '| xboxEnableAvatar: 0'
- line: '| xboxEnableKinect: 0'
- line: '| xboxEnableKinectAutoTracking: 0'
- line: '| xboxEnableFitness: 0'
- line: '| visibleInBackground: 0'
- line: '| allowFullscreenSwitch: 1'
- line: '| fullscreenMode: 3'
- line: '| xboxSpeechDB: 0'
- line: '| xboxEnableHeadOrientation: 0'
- line: '| xboxEnableGuest: 0'
- line: '| xboxEnablePIXSampling: 0'
- line: '| metalFramebufferOnly: 0'
- line: '| xboxOneResolution: 0'
- line: '| xboxOneSResolution: 0'
- line: '| xboxOneXResolution: 3'
- line: '| xboxOneMonoLoggingLevel: 0'
- line: '| xboxOneLoggingLevel: 1'
- line: '| xboxOneDisableEsram: 0'
- line: '| xboxOneEnableTypeOptimization: 0'
- line: '| xboxOnePresentImmediateThreshold: 0'
- line: '| switchQueueCommandMemory: 0'
- line: '| switchQueueControlMemory: 16384'
- line: '| switchQueueComputeMemory: 262144'
- line: '| switchNVNShaderPoolsGranularity: 33554432'
- line: '| switchNVNDefaultPoolsGranularity: 16777216'
- line: '| switchNVNOtherPoolsGranularity: 16777216'
- line: '| switchGpuScratchPoolGranularity: 2097152'
- line: '| switchAllowGpuScratchShrinking: 0'
- line: '| switchNVNMaxPublicTextureIDCount: 0'
- line: '| switchNVNMaxPublicSamplerIDCount: 0'
- line: '| switchMaxWorkerMultiple: 8'
- line: '| switchNVNGraphicsFirmwareMemory: 32'
- line: '| vulkanNumSwapchainBuffers: 3'
- line: '| vulkanEnableSetSRGBWrite: 0'
- line: '| vulkanEnablePreTransform: 0'
- line: '| vulkanEnableLateAcquireNextImage: 0'
- line: '| vulkanEnableCommandBufferRecycling: 1'
- line: '| loadStoreDebugModeEnabled: 0'
- line: '| visionOSBundleVersion: 1.0'
- line: '| tvOSBundleVersion: 1.0'
- line: '| bundleVersion: 1.0'
- line: '| preloadedAssets: []'
- line: '| metroInputSource: 0'
- line: '| wsaTransparentSwapchain: 0'
- line: '| m_HolographicPauseOnTrackingLoss: 1'
- line: '| xboxOneDisableKinectGpuReservation: 0'
- line: '| xboxOneEnable7thCore: 0'
- line: '| vrSettings:'
- line: '| enable360StereoCapture: 0'
- line: '| isWsaHolographicRemotingEnabled: 0'
- line: '| enableFrameTimingStats: 0'
- line: '| enableOpenGLProfilerGPURecorders: 1'
- line: '| allowHDRDisplaySupport: 0'
- line: '| useHDRDisplay: 0'
- line: '| hdrBitDepth: 0'
- line: '| m_ColorGamuts: 00000000'
- line: '| targetPixelDensity: 30'
- line: '| resolutionScalingMode: 0'
- line: '| resetResolutionOnWindowResize: 0'
- line: '| androidSupportedAspectRatio: 1'
- line: '| androidMaxAspectRatio: 2.1'
- line: '| androidMinAspectRatio: 1'
- line: '| applicationIdentifier:'
- line: '| Android: com.test.ET'
- line: '| Standalone: com.test.ET'
- line: '| Tizen: com.Company.ProductName'
- line: '| iPhone: com.test.ET'
- line: '| tvOS: com.Company.ProductName'
- line: '| buildNumber:'
- line: '| Standalone: 0'
- line: '| VisionOS: 0'
- line: '| iPhone: 0'
- line: '| tvOS: 0'
- line: '| overrideDefaultApplicationIdentifier: 0'
- line: '| AndroidBundleVersionCode: 1'
- line: '| AndroidMinSdkVersion: 23'
- line: '| AndroidTargetSdkVersion: 0'
- line: '| AndroidPreferredInstallLocation: 1'
- line: '| aotOptions: '
- line: '| stripEngineCode: 0'
- line: '| iPhoneStrippingLevel: 0'
- line: '| iPhoneScriptCallOptimization: 0'
- line: '| ForceInternetPermission: 0'
- line: '| ForceSDCardPermission: 0'
- line: '| CreateWallpaper: 0'
- line: '| androidSplitApplicationBinary: 0'
- line: '| keepLoadedShadersAlive: 0'
- line: '| StripUnusedMeshComponents: 0'
- line: '| strictShaderVariantMatching: 0'
- line: '| VertexChannelCompressionMask: 214'
- line: '| iPhoneSdkVersion: 988'
- line: '| iOSSimulatorArchitecture: 0'
- line: '| iOSTargetOSVersionString: 13.0'
- line: '| tvOSSdkVersion: 0'
- line: '| tvOSSimulatorArchitecture: 0'
- line: '| tvOSRequireExtendedGameController: 0'
- line: '| tvOSTargetOSVersionString: 13.0'
- line: '| VisionOSSdkVersion: 0'
- line: '| VisionOSTargetOSVersionString: 1.0'
- line: '| uIPrerenderedIcon: 0'
- line: '| uIRequiresPersistentWiFi: 0'
- line: '| uIRequiresFullScreen: 1'
- line: '| uIStatusBarHidden: 1'
- line: '| uIExitOnSuspend: 0'
- line: '| uIStatusBarStyle: 0'
- line: '| appleTVSplashScreen: {instanceID: 0}'
- line: '| appleTVSplashScreen2x: {instanceID: 0}'
- line: '| tvOSSmallIconLayers: []'
- line: '| tvOSSmallIconLayers2x: []'
- line: '| tvOSLargeIconLayers: []'
- line: '| tvOSLargeIconLayers2x: []'
- line: '| tvOSTopShelfImageLayers: []'
- line: '| tvOSTopShelfImageLayers2x: []'
- line: '| tvOSTopShelfImageWideLayers: []'
- line: '| tvOSTopShelfImageWideLayers2x: []'
- line: '| iOSLaunchScreenType: 0'
- line: '| iOSLaunchScreenPortrait: {instanceID: 0}'
- line: '| iOSLaunchScreenLandscape: {instanceID: 0}'
- line: '| iOSLaunchScreenBackgroundColor:'
- line: '| serializedVersion: 2'
- line: '| rgba: 0'
- line: '| iOSLaunchScreenFillPct: 100'
- line: '| iOSLaunchScreenSize: 100'
- line: '| iOSLaunchScreeniPadType: 0'
- line: '| iOSLaunchScreeniPadImage: {instanceID: 0}'
- line: '| iOSLaunchScreeniPadBackgroundColor:'
- line: '| serializedVersion: 2'
- line: '| rgba: 0'
- line: '| iOSLaunchScreeniPadFillPct: 100'
- line: '| iOSLaunchScreeniPadSize: 100'
- line: '| iOSLaunchScreenCustomStoryboardPath: '
- line: '| iOSLaunchScreeniPadCustomStoryboardPath: '
- line: '| iOSDeviceRequirements: []'
- line: '| iOSURLSchemes: []'
- line: '| macOSURLSchemes: []'
- line: '| iOSBackgroundModes: 0'
- line: '| iOSMetalForceHardShadows: 0'
- line: '| metalEditorSupport: 1'
- line: '| metalAPIValidation: 1'
- line: '| metalCompileShaderBinary: 0'
- line: '| iOSRenderExtraFrameOnPause: 1'
- line: '| iosCopyPluginsCodeInsteadOfSymlink: 0'
- line: '| appleDeveloperTeamID: '
- line: '| iOSManualSigningProvisioningProfileID: '
- line: '| tvOSManualSigningProvisioningProfileID: '
- line: '| VisionOSManualSigningProvisioningProfileID: '
- line: '| iOSManualSigningProvisioningProfileType: 0'
- line: '| tvOSManualSigningProvisioningProfileType: 0'
- line: '| VisionOSManualSigningProvisioningProfileType: 0'
- line: '| appleEnableAutomaticSigning: 0'
- line: '| iOSRequireARKit: 0'
- line: '| iOSAutomaticallyDetectAndAddCapabilities: 1'
- line: '| appleEnableProMotion: 0'
- line: '| shaderPrecisionModel: 0'
- line: '| clonedFromGUID: 00000000000000000000000000000000'
- line: '| templatePackageId: '
- line: '| templateDefaultScene: '
- line: '| useCustomMainManifest: 0'
- line: '| useCustomLauncherManifest: 0'
- line: '| useCustomMainGradleTemplate: 0'
- line: '| useCustomLauncherGradleManifest: 0'
- line: '| useCustomBaseGradleTemplate: 0'
- line: '| useCustomGradlePropertiesTemplate: 0'
- line: '| useCustomGradleSettingsTemplate: 0'
- line: '| useCustomProguardFile: 0'
- line: '| AndroidTargetArchitectures: 2'
- line: '| AndroidSplashScreenScale: 0'
- line: '| androidSplashScreen: {instanceID: 0}'
- line: '| AndroidKeystoreName: '
- line: '| AndroidKeyaliasName: '
- line: '| AndroidEnableArmv9SecurityFeatures: 0'
- line: '| AndroidEnableArm64MTE: 0'
- line: '| AndroidBuildApkPerCpuArchitecture: 0'
- line: '| AndroidTVCompatibility: 1'
- line: '| AndroidIsGame: 1'
- line: '| AndroidEnableTango: 0'
- line: '| androidEnableBanner: 1'
- line: '| androidUseLowAccuracyLocation: 0'
- line: '| androidUseCustomKeystore: 0'
- line: '| m_AndroidBanners:'
- line: '| - width: 320'
- line: '| height: 180'
- line: '| banner: {instanceID: 0}'
- line: '| androidGamepadSupportLevel: 0'
- line: '| AndroidMinifyRelease: 0'
- line: '| AndroidMinifyDebug: 0'
- line: '| AndroidValidateAppBundleSize: 1'
- line: '| AndroidAppBundleSizeToValidate: 150'
- line: '| AndroidReportGooglePlayAppDependencies: 1'
- line: '| androidSymbolsSizeThreshold: 800'
- line: '| m_BuildTargetIcons:'
- line: '| - m_BuildTarget: '
- line: '| m_Icons:'
- line: '| - serializedVersion: 2'
- line: '| m_Icon: {instanceID: 0}'
- line: '| m_Width: 128'
- line: '| m_Height: 128'
- line: '| m_Kind: 0'
- line: '| m_BuildTargetPlatformIcons:'
- line: '| - m_BuildTarget: Android'
- line: '| m_Icons:'
- line: '| - m_Textures: []'
- line: '| m_Width: 432'
- line: '| m_Height: 432'
- line: '| m_Kind: 2'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 324'
- line: '| m_Height: 324'
- line: '| m_Kind: 2'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 216'
- line: '| m_Height: 216'
- line: '| m_Kind: 2'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 162'
- line: '| m_Height: 162'
- line: '| m_Kind: 2'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 108'
- line: '| m_Height: 108'
- line: '| m_Kind: 2'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 81'
- line: '| m_Height: 81'
- line: '| m_Kind: 2'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 192'
- line: '| m_Height: 192'
- line: '| m_Kind: 0'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 144'
- line: '| m_Height: 144'
- line: '| m_Kind: 0'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 96'
- line: '| m_Height: 96'
- line: '| m_Kind: 0'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 72'
- line: '| m_Height: 72'
- line: '| m_Kind: 0'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 48'
- line: '| m_Height: 48'
- line: '| m_Kind: 0'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 36'
- line: '| m_Height: 36'
- line: '| m_Kind: 0'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 192'
- line: '| m_Height: 192'
- line: '| m_Kind: 1'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 144'
- line: '| m_Height: 144'
- line: '| m_Kind: 1'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 96'
- line: '| m_Height: 96'
- line: '| m_Kind: 1'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 72'
- line: '| m_Height: 72'
- line: '| m_Kind: 1'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 48'
- line: '| m_Height: 48'
- line: '| m_Kind: 1'
- line: '| m_SubKind: '
- line: '| - m_Textures: []'
- line: '| m_Width: 36'
- line: '| m_Height: 36'
- line: '| m_Kind: 1'
- line: '| m_SubKind: '
- line: '| - m_BuildTarget: iPhone'
- line: '| m_Icons:'
- line: '| - m_Textures: []'
- line: '| m_Width: 180'
- line: '| m_Height: 180'
- line: '| m_Kind: 0'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 120'
- line: '| m_Height: 120'
- line: '| m_Kind: 0'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 167'
- line: '| m_Height: 167'
- line: '| m_Kind: 0'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 152'
- line: '| m_Height: 152'
- line: '| m_Kind: 0'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 76'
- line: '| m_Height: 76'
- line: '| m_Kind: 0'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 120'
- line: '| m_Height: 120'
- line: '| m_Kind: 3'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 80'
- line: '| m_Height: 80'
- line: '| m_Kind: 3'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 80'
- line: '| m_Height: 80'
- line: '| m_Kind: 3'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 40'
- line: '| m_Height: 40'
- line: '| m_Kind: 3'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 87'
- line: '| m_Height: 87'
- line: '| m_Kind: 1'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 58'
- line: '| m_Height: 58'
- line: '| m_Kind: 1'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 29'
- line: '| m_Height: 29'
- line: '| m_Kind: 1'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 58'
- line: '| m_Height: 58'
- line: '| m_Kind: 1'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 29'
- line: '| m_Height: 29'
- line: '| m_Kind: 1'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 60'
- line: '| m_Height: 60'
- line: '| m_Kind: 2'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 40'
- line: '| m_Height: 40'
- line: '| m_Kind: 2'
- line: '| m_SubKind: iPhone'
- line: '| - m_Textures: []'
- line: '| m_Width: 40'
- line: '| m_Height: 40'
- line: '| m_Kind: 2'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 20'
- line: '| m_Height: 20'
- line: '| m_Kind: 2'
- line: '| m_SubKind: iPad'
- line: '| - m_Textures: []'
- line: '| m_Width: 1024'
- line: '| m_Height: 1024'
- line: '| m_Kind: 4'
- line: '| m_SubKind: App Store'
- line: '| m_BuildTargetBatching: []'
- line: '| m_BuildTargetShaderSettings: []'
- line: '| m_BuildTargetGraphicsJobs:'
- line: '| - m_BuildTarget: MacStandaloneSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: Switch'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: MetroSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: AppleTVSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: BJMSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: LinuxStandaloneSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: PS4Player'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: iOSSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: WindowsStandaloneSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: XboxOnePlayer'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: LuminSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: CloudRendering'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: AndroidPlayer'
- line: '| m_GraphicsJobs: 0'
- line: '| - m_BuildTarget: WebGLSupport'
- line: '| m_GraphicsJobs: 0'
- line: '| m_BuildTargetGraphicsJobMode:'
- line: '| - m_BuildTarget: PS4Player'
- line: '| m_GraphicsJobMode: 0'
- line: '| - m_BuildTarget: XboxOnePlayer'
- line: '| m_GraphicsJobMode: 0'
- line: '| m_BuildTargetGraphicsAPIs:'
- line: '| - m_BuildTarget: AndroidPlayer'
- line: '| m_APIs: 0b000000'
- line: '| m_Automatic: 0'
- line: '| - m_BuildTarget: iOSSupport'
- line: '| m_APIs: 10000000'
- line: '| m_Automatic: 1'
- line: '| m_BuildTargetVRSettings:'
- line: '| - m_BuildTarget: Android'
- line: '| m_Enabled: 0'
- line: '| m_Devices:'
- line: '| - Oculus'
- line: '| - m_BuildTarget: Windows Store Apps'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: N3DS'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: PS3'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: PS4'
- line: '| m_Enabled: 0'
- line: '| m_Devices:'
- line: '| - PlayStationVR'
- line: '| - m_BuildTarget: PSM'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: PSP2'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: SamsungTV'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: Standalone'
- line: '| m_Enabled: 0'
- line: '| m_Devices:'
- line: '| - Oculus'
- line: '| - m_BuildTarget: Tizen'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: WebGL'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: WebPlayer'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: WiiU'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: Xbox360'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: XboxOne'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: iPhone'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| - m_BuildTarget: tvOS'
- line: '| m_Enabled: 0'
- line: '| m_Devices: []'
- line: '| m_DefaultShaderChunkSizeInMB: 16'
- line: '| m_DefaultShaderChunkCount: 0'
- line: '| openGLRequireES31: 1'
- line: '| openGLRequireES31AEP: 0'
- line: '| openGLRequireES32: 0'
- line: '| m_TemplateCustomTags: {}'
- line: '| mobileMTRendering:'
- line: '| Android: 1'
- line: '| iPhone: 1'
- line: '| tvOS: 1'
- line: '| m_BuildTargetGroupLightmapEncodingQuality:'
- line: '| - serializedVersion: 2'
- line: '| m_BuildTarget: Standalone'
- line: '| m_EncodingQuality: 1'
- line: '| - serializedVersion: 2'
- line: '| m_BuildTarget: XboxOne'
- line: '| m_EncodingQuality: 1'
- line: '| - serializedVersion: 2'
- line: '| m_BuildTarget: PS4'
- line: '| m_EncodingQuality: 1'
- line: '| m_BuildTargetGroupLightmapSettings: []'
- line: '| m_BuildTargetGroupLoadStoreDebugModeSettings: []'
- line: '| m_BuildTargetNormalMapEncoding: []'
- line: '| m_BuildTargetDefaultTextureCompressionFormat: []'
- line: '| playModeTestRunnerEnabled: 0'
- line: '| runPlayModeTestAsEditModeTest: 0'
- line: '| actionOnDotNetUnhandledException: 1'
- line: '| editorGfxJobOverride: 1'
- line: '| enableInternalProfiler: 0'
- line: '| logObjCUncaughtExceptions: 1'
- line: '| enableCrashReportAPI: 0'
- line: '| cameraUsageDescription: '
- line: '| locationUsageDescription: '
- line: '| microphoneUsageDescription: '
- line: '| bluetoothUsageDescription: '
- line: '| macOSTargetOSVersion: 11.0'
- line: '| switchNMETAOverride: '
- line: '| switchNetLibKey: '
- line: '| switchSocketMemoryPoolSize: 6144'
- line: '| switchSocketAllocatorPoolSize: 128'
- line: '| switchSocketConcurrencyLimit: 14'
- line: '| switchScreenResolutionBehavior: 2'
- line: '| switchUseCPUProfiler: 0'
- line: '| switchEnableFileSystemTrace: 0'
- line: '| switchLTOSetting: 0'
- line: '| switchApplicationID: 0x01004b9000490000'
- line: '| switchNSODependencies: '
- line: '| switchCompilerFlags: '
- line: '| switchTitleNames_0: '
- line: '| switchTitleNames_1: '
- line: '| switchTitleNames_2: '
- line: '| switchTitleNames_3: '
- line: '| switchTitleNames_4: '
- line: '| switchTitleNames_5: '
- line: '| switchTitleNames_6: '
- line: '| switchTitleNames_7: '
- line: '| switchTitleNames_8: '
- line: '| switchTitleNames_9: '
- line: '| switchTitleNames_10: '
- line: '| switchTitleNames_11: '
- line: '| switchTitleNames_12: '
- line: '| switchTitleNames_13: '
- line: '| switchTitleNames_14: '
- line: '| switchTitleNames_15: '
- line: '| switchPublisherNames_0: '
- line: '| switchPublisherNames_1: '
- line: '| switchPublisherNames_2: '
- line: '| switchPublisherNames_3: '
- line: '| switchPublisherNames_4: '
- line: '| switchPublisherNames_5: '
- line: '| switchPublisherNames_6: '
- line: '| switchPublisherNames_7: '
- line: '| switchPublisherNames_8: '
- line: '| switchPublisherNames_9: '
- line: '| switchPublisherNames_10: '
- line: '| switchPublisherNames_11: '
- line: '| switchPublisherNames_12: '
- line: '| switchPublisherNames_13: '
- line: '| switchPublisherNames_14: '
- line: '| switchPublisherNames_15: '
- line: '| switchIcons_0: {instanceID: 0}'
- line: '| switchIcons_1: {instanceID: 0}'
- line: '| switchIcons_2: {instanceID: 0}'
- line: '| switchIcons_3: {instanceID: 0}'
- line: '| switchIcons_4: {instanceID: 0}'
- line: '| switchIcons_5: {instanceID: 0}'
- line: '| switchIcons_6: {instanceID: 0}'
- line: '| switchIcons_7: {instanceID: 0}'
- line: '| switchIcons_8: {instanceID: 0}'
- line: '| switchIcons_9: {instanceID: 0}'
- line: '| switchIcons_10: {instanceID: 0}'
- line: '| switchIcons_11: {instanceID: 0}'
- line: '| switchIcons_12: {instanceID: 0}'
- line: '| switchIcons_13: {instanceID: 0}'
- line: '| switchIcons_14: {instanceID: 0}'
- line: '| switchIcons_15: {instanceID: 0}'
- line: '| switchSmallIcons_0: {instanceID: 0}'
- line: '| switchSmallIcons_1: {instanceID: 0}'
- line: '| switchSmallIcons_2: {instanceID: 0}'
- line: '| switchSmallIcons_3: {instanceID: 0}'
- line: '| switchSmallIcons_4: {instanceID: 0}'
- line: '| switchSmallIcons_5: {instanceID: 0}'
- line: '| switchSmallIcons_6: {instanceID: 0}'
- line: '| switchSmallIcons_7: {instanceID: 0}'
- line: '| switchSmallIcons_8: {instanceID: 0}'
- line: '| switchSmallIcons_9: {instanceID: 0}'
- line: '| switchSmallIcons_10: {instanceID: 0}'
- line: '| switchSmallIcons_11: {instanceID: 0}'
- line: '| switchSmallIcons_12: {instanceID: 0}'
- line: '| switchSmallIcons_13: {instanceID: 0}'
- line: '| switchSmallIcons_14: {instanceID: 0}'
- line: '| switchSmallIcons_15: {instanceID: 0}'
- line: '| switchManualHTML: '
- line: '| switchAccessibleURLs: '
- line: '| switchLegalInformation: '
- line: '| switchMainThreadStackSize: 1048576'
- line: '| switchPresenceGroupId: 0x01004b9000490000'
- line: '| switchLogoHandling: 0'
- line: '| switchReleaseVersion: 0'
- line: '| switchDisplayVersion: 1.0.0'
- line: '| switchStartupUserAccount: 0'
- line: '| switchSupportedLanguagesMask: 0'
- line: '| switchLogoType: 0'
- line: '| switchApplicationErrorCodeCategory: '
- line: '| switchUserAccountSaveDataSize: 0'
- line: '| switchUserAccountSaveDataJournalSize: 0'
- line: '| switchApplicationAttribute: 0'
- line: '| switchCardSpecSize: 4'
- line: '| switchCardSpecClock: 25'
- line: '| switchRatingsMask: 0'
- line: '| switchRatingsInt_0: 0'
- line: '| switchRatingsInt_1: 0'
- line: '| switchRatingsInt_2: 0'
- line: '| switchRatingsInt_3: 0'
- line: '| switchRatingsInt_4: 0'
- line: '| switchRatingsInt_5: 0'
- line: '| switchRatingsInt_6: 0'
- line: '| switchRatingsInt_7: 0'
- line: '| switchRatingsInt_8: 0'
- line: '| switchRatingsInt_9: 0'
- line: '| switchRatingsInt_10: 0'
- line: '| switchRatingsInt_11: 0'
- line: '| switchRatingsInt_12: 0'
- line: '| switchLocalCommunicationIds_0: 0x01004b9000490000'
- line: '| switchLocalCommunicationIds_1: '
- line: '| switchLocalCommunicationIds_2: '
- line: '| switchLocalCommunicationIds_3: '
- line: '| switchLocalCommunicationIds_4: '
- line: '| switchLocalCommunicationIds_5: '
- line: '| switchLocalCommunicationIds_6: '
- line: '| switchLocalCommunicationIds_7: '
- line: '| switchParentalControl: 0'
- line: '| switchAllowsScreenshot: 1'
- line: '| switchAllowsVideoCapturing: 1'
- line: '| switchAllowsRuntimeAddOnContentInstall: 0'
- line: '| switchDataLossConfirmation: 0'
- line: '| switchUserAccountLockEnabled: 0'
- line: '| switchSystemResourceMemory: 16777216'
- line: '| switchSupportedNpadStyles: 3'
- line: '| switchNativeFsCacheSize: 32'
- line: '| switchIsHoldTypeHorizontal: 0'
- line: '| switchSupportedNpadCount: 8'
- line: '| switchEnableTouchScreen: 1'
- line: '| switchSocketConfigEnabled: 0'
- line: '| switchTcpInitialSendBufferSize: 32'
- line: '| switchTcpInitialReceiveBufferSize: 64'
- line: '| switchTcpAutoSendBufferSizeMax: 256'
- line: '| switchTcpAutoReceiveBufferSizeMax: 256'
- line: '| switchUdpSendBufferSize: 9'
- line: '| switchUdpReceiveBufferSize: 42'
- line: '| switchSocketBufferEfficiency: 4'
- line: '| switchSocketInitializeEnabled: 1'
- line: '| switchNetworkInterfaceManagerInitializeEnabled: 1'
- line: '| switchDisableHTCSPlayerConnection: 0'
- line: '| switchUseNewStyleFilepaths: 0'
- line: '| switchUseLegacyFmodPriorities: 1'
- line: '| switchUseMicroSleepForYield: 1'
- line: '| switchEnableRamDiskSupport: 0'
- line: '| switchMicroSleepForYieldTime: 25'
- line: '| switchRamDiskSpaceSize: 12'
- line: '| switchUpgradedPlayerSettingsToNMETA: 0'
- line: '| ps4NPAgeRating: 12'
- line: '| ps4NPTitleSecret: '
- line: '| ps4NPTrophyPackPath: '
- line: '| ps4ParentalLevel: 1'
- line: '| ps4ContentID: ED1633-NPXX51362_00-0000000000000000'
- line: '| ps4Category: 0'
- line: '| ps4MasterVersion: 01.00'
- line: '| ps4AppVersion: 01.00'
- line: '| ps4AppType: 0'
- line: '| ps4ParamSfxPath: '
- line: '| ps4VideoOutPixelFormat: 0'
- line: '| ps4VideoOutInitialWidth: 1920'
- line: '| ps4VideoOutBaseModeInitialWidth: 1920'
- line: '| ps4VideoOutReprojectionRate: 120'
- line: '| ps4PronunciationXMLPath: '
- line: '| ps4PronunciationSIGPath: '
- line: '| ps4BackgroundImagePath: '
- line: '| ps4StartupImagePath: '
- line: '| ps4StartupImagesFolder: '
- line: '| ps4IconImagesFolder: '
- line: '| ps4SaveDataImagePath: '
- line: '| ps4SdkOverride: '
- line: '| ps4BGMPath: '
- line: '| ps4ShareFilePath: '
- line: '| ps4ShareOverlayImagePath: '
- line: '| ps4PrivacyGuardImagePath: '
- line: '| ps4ExtraSceSysFile: '
- line: '| ps4NPtitleDatPath: '
- line: '| ps4RemotePlayKeyAssignment: -1'
- line: '| ps4RemotePlayKeyMappingDir: '
- line: '| ps4PlayTogetherPlayerCount: 0'
- line: '| ps4EnterButtonAssignment: 1'
- line: '| ps4ApplicationParam1: 0'
- line: '| ps4ApplicationParam2: 0'
- line: '| ps4ApplicationParam3: 0'
- line: '| ps4ApplicationParam4: 0'
- line: '| ps4DownloadDataSize: 0'
- line: '| ps4GarlicHeapSize: 2048'
- line: '| ps4ProGarlicHeapSize: 2560'
- line: '| playerPrefsMaxSize: 32768'
- line: '| ps4Passcode: eaoEiIgxIX4a2dREbbSqWy6yhKIDCdJO'
- line: '| ps4pnSessions: 1'
- line: '| ps4pnPresence: 1'
- line: '| ps4pnFriends: 1'
- line: '| ps4pnGameCustomData: 1'
- line: '| playerPrefsSupport: 0'
- line: '| enableApplicationExit: 0'
- line: '| resetTempFolder: 1'
- line: '| restrictedAudioUsageRights: 0'
- line: '| ps4UseResolutionFallback: 0'
- line: '| ps4ReprojectionSupport: 0'
- line: '| ps4UseAudio3dBackend: 0'
- line: '| ps4UseLowGarlicFragmentationMode: 1'
- line: '| ps4SocialScreenEnabled: 0'
- line: '| ps4ScriptOptimizationLevel: 3'
- line: '| ps4Audio3dVirtualSpeakerCount: 14'
- line: '| ps4attribCpuUsage: 0'
- line: '| ps4PatchPkgPath: '
- line: '| ps4PatchLatestPkgPath: '
- line: '| ps4PatchChangeinfoPath: '
- line: '| ps4PatchDayOne: 0'
- line: '| ps4attribUserManagement: 0'
- line: '| ps4attribMoveSupport: 0'
- line: '| ps4attrib3DSupport: 0'
- line: '| ps4attribShareSupport: 0'
- line: '| ps4attribExclusiveVR: 0'
- line: '| ps4disableAutoHideSplash: 0'
- line: '| ps4videoRecordingFeaturesUsed: 0'
- line: '| ps4contentSearchFeaturesUsed: 0'
- line: '| ps4CompatibilityPS5: 0'
- line: '| ps4AllowPS5Detection: 0'
- line: '| ps4GPU800MHz: 1'
- line: '| ps4attribEyeToEyeDistanceSettingVR: 0'
- line: '| ps4IncludedModules: []'
- line: '| ps4attribVROutputEnabled: 0'
- line: '| monoEnv: '
- line: '| splashScreenBackgroundSourceLandscape: {instanceID: 0}'
- line: '| splashScreenBackgroundSourcePortrait: {instanceID: 0}'
- line: '| blurSplashScreenBackground: 1'
- line: '| spritePackerPolicy: '
- line: '| webGLMemorySize: 256'
- line: '| webGLExceptionSupport: 1'
- line: '| webGLNameFilesAsHashes: 0'
- line: '| webGLShowDiagnostics: 0'
- line: '| webGLDataCaching: 0'
- line: '| webGLDebugSymbols: 0'
- line: '| webGLEmscriptenArgs: '
- line: '| webGLModulesDirectory: '
- line: '| webGLTemplate: APPLICATION:Default'
- line: '| webGLAnalyzeBuildSize: 0'
- line: '| webGLUseEmbeddedResources: 0'
- line: '| webGLCompressionFormat: 1'
- line: '| webGLWasmArithmeticExceptions: 0'
- line: '| webGLLinkerTarget: 1'
- line: '| webGLThreadsSupport: 0'
- line: '| webGLDecompressionFallback: 0'
- line: '| webGLInitialMemorySize: 32'
- line: '| webGLMaximumMemorySize: 2048'
- line: '| webGLMemoryGrowthMode: 2'
- line: '| webGLMemoryLinearGrowthStep: 16'
- line: '| webGLMemoryGeometricGrowthStep: 0.2'
- line: '| webGLMemoryGeometricGrowthCap: 96'
- line: '| webGLEnableWebGPU: 0'
- line: '| webGLPowerPreference: 2'
- line: '| webGLWebAssemblyTable: 0'
- line: '| webGLWebAssemblyBigInt: 0'
- line: '| webGLCloseOnQuit: 0'
- line: '| webWasm2023: 0'
- line: '| scriptingDefineSymbols:'
- line: '| Android: UNITY;ENABLE_VIEW'
- line: '| Server: UNITY;ENABLE_VIEW'
- line: '| Standalone: UNITY;ENABLE_VIEW'
- line: '| WebGL: UNITY;ENABLE_VIEW'
- line: '| iPhone: UNITY;ENABLE_VIEW'
- line: '| additionalCompilerArguments: {}'
- line: '| platformArchitecture:'
- line: '| iPhone: 1'
- line: '| scriptingBackend:'
- line: '| Android: 1'
- line: '| Server: 0'
- line: '| Standalone: 1'
- line: '| iPhone: 1'
- line: '| il2cppCompilerConfiguration: {}'
- line: '| il2cppCodeGeneration: {}'
- line: '| il2cppStacktraceInformation: {}'
- line: '| managedStrippingLevel:'
- line: '| Android: 1'
- line: '| EmbeddedLinux: 1'
- line: '| GameCoreScarlett: 1'
- line: '| GameCoreXboxOne: 1'
- line: '| Lumin: 1'
- line: '| Nintendo Switch: 1'
- line: '| PS4: 1'
- line: '| PS5: 1'
- line: '| Stadia: 1'
- line: '| Standalone: 1'
- line: '| WebGL: 1'
- line: '| Windows Store Apps: 1'
- line: '| XboxOne: 1'
- line: '| iPhone: 1'
- line: '| tvOS: 1'
- line: '| incrementalIl2cppBuild: {}'
- line: '| suppressCommonWarnings: 1'
- line: '| allowUnsafeCode: 1'
- line: '| useDeterministicCompilation: 1'
- line: '| additionalIl2CppArgs: '
- line: '| scriptingRuntimeVersion: 1'
- line: '| gcIncremental: 0'
- line: '| gcWBarrierValidation: 0'
- line: '| apiCompatibilityLevelPerPlatform:'
- line: '| Android: 3'
- line: '| Server: 3'
- line: '| Standalone: 3'
- line: '| editorAssembliesCompatibilityLevel: 2'
- line: '| m_RenderingPath: 1'
- line: '| m_MobileRenderingPath: 1'
- line: '| metroPackageName: Unity'
- line: '| metroPackageVersion: '
- line: '| metroCertificatePath: '
- line: '| metroCertificatePassword: '
- line: '| metroCertificateSubject: '
- line: '| metroCertificateIssuer: '
- line: '| metroCertificateNotAfter: 0000000000000000'
- line: '| metroApplicationDescription: Unity'
- line: '| wsaImages: {}'
- line: '| metroTileShortName: '
- line: '| metroTileShowName: 0'
- line: '| metroMediumTileShowName: 0'
- line: '| metroLargeTileShowName: 0'
- line: '| metroWideTileShowName: 0'
- line: '| metroSupportStreamingInstall: 0'
- line: '| metroLastRequiredScene: 0'
- line: '| metroDefaultTileSize: 1'
- line: '| metroTileForegroundText: 1'
- line: '| metroTileBackgroundColor: {r: 0, g: 0, b: 0, a: 1}'
- line: '| metroSplashScreenBackgroundColor: {r: 0, g: 0, b: 0, a: 1}'
- line: '| metroSplashScreenUseBackgroundColor: 0'
- line: '| syncCapabilities: 0'
- line: '| platformCapabilities: {}'
- line: '| metroTargetDeviceFamilies: {}'
- line: '| metroFTAName: '
- line: '| metroFTAFileTypes: []'
- line: '| metroProtocolName: '
- line: '| vcxProjDefaultLanguage: '
- line: '| XboxOneProductId: '
- line: '| XboxOneUpdateKey: '
- line: '| XboxOneSandboxId: '
- line: '| XboxOneContentId: '
- line: '| XboxOneTitleId: '
- line: '| XboxOneSCId: '
- line: '| XboxOneGameOsOverridePath: '
- line: '| XboxOnePackagingOverridePath: '
- line: '| XboxOneAppManifestOverridePath: '
- line: '| XboxOneVersion: 1.0.0.0'
- line: '| XboxOnePackageEncryption: 0'
- line: '| XboxOnePackageUpdateGranularity: 2'
- line: '| XboxOneDescription: '
- line: '| XboxOneLanguage:'
- line: '| - enus'
- line: '| XboxOneCapability: []'
- line: '| XboxOneGameRating: {}'
- line: '| XboxOneIsContentPackage: 0'
- line: '| XboxOneEnhancedXboxCompatibilityMode: 0'
- line: '| XboxOneEnableGPUVariability: 0'
- line: '| XboxOneSockets: {}'
- line: '| XboxOneSplashScreen: {instanceID: 0}'
- line: '| XboxOneAllowedProductIds: []'
- line: '| XboxOnePersistentLocalStorageSize: 0'
- line: '| XboxOneXTitleMemory: 8'
- line: '| XboxOneOverrideIdentityName: '
- line: '| XboxOneOverrideIdentityPublisher: '
- line: '| vrEditorSettings: {}'
- line: '| cloudServicesEnabled:'
- line: '| Analytics: 0'
- line: '| Build: 0'
- line: '| Collab: 0'
- line: '| ErrorHub: 0'
- line: '| Game_Performance: 0'
- line: '| Hub: 0'
- line: '| Purchasing: 0'
- line: '| UNet: 0'
- line: '| Unity_Ads: 0'
- line: '| luminIcon:'
- line: '| m_Name: '
- line: '| m_ModelFolderPath: '
- line: '| m_PortalFolderPath: '
- line: '| luminCert:'
- line: '| m_CertPath: '
- line: '| m_SignPackage: 1'
- line: '| luminIsChannelApp: 0'
- line: '| luminVersion:'
- line: '| m_VersionCode: 1'
- line: '| m_VersionName: '
- line: '| hmiPlayerDataPath: '
- line: '| hmiForceSRGBBlit: 1'
- line: '| embeddedLinuxEnableGamepadInput: 1'
- line: '| hmiCpuConfiguration: '
- line: '| hmiLogStartupTiming: 0'
- line: '| qnxGraphicConfPath: '
- line: '| apiCompatibilityLevel: 3'
- line: '| captureStartupLogs: {}'
- line: '| activeInputHandler: 0'
- line: '| windowsGamepadBackendHint: 0'
- line: '| cloudProjectId: '
- line: '| framebufferDepthMemorylessMode: 0'
- line: '| qualitySettingsNames: []'
- line: '| projectName: '
- line: '| organizationId: '
- line: '| cloudEnabled: 0'
- line: '| legacyClampBlendShapeWeights: 0'
- line: '| hmiLoadingImage: {instanceID: 0}'
- line: '| platformRequiresReadableAssets: 0'
- line: '| virtualTexturingSupportEnabled: 0'
- line: '| insecureHttpOption: 0'
- line: '| androidVulkanDenyFilterList: []'
- line: '| androidVulkanAllowFilterList: []'
- line: '| '
references:
version: 2
RefIds:
- rid: 6418588523325555037
type: {class: OSXStandaloneBuildProfile, ns: UnityEditor.OSXStandalone, asm: UnityEditor.OSXStandalone.Extensions}
data:
m_Development: 0
m_ConnectProfiler: 0
m_BuildWithDeepProfilingSupport: 0
m_AllowDebugging: 0
m_WaitForManagedDebugger: 0
m_ManagedDebuggerFixedPort: 0
m_ExplicitNullChecks: 0
m_ExplicitDivideByZeroChecks: 0
m_ExplicitArrayBoundsChecks: 0
m_CompressionType: 0
m_InstallInBuildFolder: 0
m_MacOSXcodeBuildConfig: 1
m_Architecture: 2
m_CreateXcodeProject: 0
================================================
FILE: Assets/Settings/Build Profiles/Mac.asset.meta
================================================
fileFormatVersion: 2
guid: b6ae1720552ce4174bde3c7ee1ff65aa
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Settings/Build Profiles.meta
================================================
fileFormatVersion: 2
guid: 1c7616f0661a042bab7d5e2ed3c6aac1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Settings.meta
================================================
fileFormatVersion: 2
guid: a9ccb235ecf71412cb659c2bd837eba0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/link.xml
================================================
<linker>
<assembly fullname="ET.Core" preserve="all"/>
<assembly fullname="ET.Loader" preserve="all"/>
<assembly fullname="UnityEngine" preserve="all"/>
<assembly fullname="System" preserve="all"/>
<assembly fullname="MongoDB.Bson" preserve="all"/>
<assembly fullname="System.Runtime.CompilerServices.Unsafe" preserve="all"/>
<assembly fullname="UnityEngine.AnimationModule" preserve="all" />
<assembly fullname="mscorlib" preserve="all" />
<assembly fullname="UnityEngine.AssetBundleModule" preserve="all" />
<assembly fullname="UnityEngine.CoreModule" preserve="all" />
<assembly fullname="UnityEngine.InputLegacyModule" preserve="all" />
</linker>
================================================
FILE: Assets/link.xml.meta
================================================
fileFormatVersion: 2
guid: 4c3a563f505464efb8376db1bae84009
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Book/1.1运行指南.md
================================================
# 运行步骤
1. IDE安装
**使用[Rider2024.3](https://www.jetbrains.com/zh-cn/rider/)**(更新到最新版),需要安装以下内容:
- windows上用visual studio安装最新的.Net8, mac请用homebrew安装.Net8跟powershell
- 不支持VS,新人用VS搞出各种问题请不要来问我,我也没用过VS,后期搞熟了可以自己改用VS
2. 出现如下报错 [Package Manager Window] Error searching for packages. 不用处理!!!!!! 这是因为github package的注册表跟unity有些不兼容导致的,忽略即可
3. 该分支必须使用Unity**6000.0.25**(初学者请在此版本用熟后再切换其他版本)
4. 整个过程请开启全局翻墙,否则各种unity包 nuget包下载不下来,报memerypack等错误
5. 注意一定要clone一个新的工程!clone一个新的工程!clone一个新的工程!重要的事情说三遍!!!
6. 启动UnityHub,打开(Open) -> 选中'ET'文件夹所在目录后打开工程,特别注意,ET9的目录结构跟ET8.1完全不同,请全新下载整个工程
7. 打开工程后,点击Unity菜单 -> Edit -> Preferences -> External Tools,点击下拉框'External ScriptEditor'选择Rider,Generate .csproj files for要勾选前两个
8. 在你的github中获取token,获取方法:打开 https://github.com/settings/tokens 选择tokens(classic),点击generate new token,下面全部勾选,点击确定,复制你的token保存
9. 打开菜单ET->Init->Manage scope registries, 点击ET-Packages Edit, 把你的github token粘贴到token里面 save,然后再看到User Credentials on this computer点+号,
Registry URL填入 https://npm.pkg.github.com/@ET-Packages
token填入你的github token
always auth 填 true
点击add
10. 在https://github.com/orgs/ET-Packages/packages 中选择一个demo包安装,目前有两个demo, cn.etetet.lockstep跟cn.etetet.statesync分别是帧同步跟状态同步(一个工程只能安装一个demo,不能同时装多个)
新手最好先选择状态同步,因为我都是先修改状态同步,偶尔会漏改了帧同步,等状态同步跑通再尝试帧同步
11. 两种安装包的方式,第一种,可以关闭Unity,打开Packages/manifest.json, 在dependencies中加上demo包跟版本号,例如"cn.etetet.lockstep": "0.0.36", 注意后面有个逗号,重启Unity,Unity就会自动下载依赖,并且会显示下载进度,该方案安装速度更快。第二种,打开Unity的菜单Windows->PackageManager,点击左上角加号,选择add package by name,输入包名,点击右边add,会把所有的依赖包下载。这种因为Unity有bug导致安装很慢。推荐使用第一种安装方式。
12. 注意,安装完成后,在PackageManager中观察一下,所有的ET开头的包都会是Custom包,这是因为安装后ET会把包挪到Packages目录,从而变成Custom包,如果有ET开头的包不是Custom,请重新点击ET->Init->RepairDependencies然后刷新Unity
13. 运行Unity菜单 ET->StateSync->Init或者ET->LockStep->Init (这一步会导Excel 导Proto 生成assemlbyreference 添加INITED宏,并且自动链接demo中的ET.sln到根目录,并且把Loader包中的GlobalConfig中的SceneName设置成demo名)
14. Unity中将Packages/ET.Loader/Resources/GlobalConfig中的CodeMode改成ClientServer,将SceneName设置成demo名StateSync或者LockStep(这个在上一步会自动设置,你这里检查一下名字是不是demo名), 这个选项是将服务端运行在Unity中,一体化运行。如果客户端单独打包的话,这里要使用Client
15. Packages/ET.YooAssets/Resources/YooConfig中将EPlayMode修改为EditorSimulateMode(具体请看YooAssets的官方文档)
16. 点击Unity菜单 Assets->Open C# Project,这里由于修改了rider插件,会自动打开ET.sln
17. 编译整个ET.sln, 注意要翻墙,否则可能nuget包下载不下来,导致编译出错(翻墙后如果还有报错解决不了可以尝试先用VS打开ET.sln编译一次后再回到Rider重新编译一次)
18. Unity中, 然后双击Packages/ET.Loader/Scenes/Init场景,点击Play(▶)即可运行
19. 帧同步默认是一个人匹配,如果需要多人匹配,修改**LSConstValue.cs**中的**MatchCount** 客户端服务端都要重新编译,都要重启即可
20. 注意要独立启动服务器,右键UnityHub,以管理员身份运行UnityHub,然后启动Unity(没有管理员启动是不行的,因为服务端要开启http服务,普通权限开不了),
停止Unity Play,点开Unity菜单->ET->Server Tools->Start Server(Single Process),这样就单独启动了服务端。打开Unity菜单 -> ET -> BuildTool中CodeMode改成Client,点击Unity Play,登录。
如果还是连接不上报10037错误,注意看ET/Logs目录,看有没有Error日志。 如果要用rider启动服务器,rider也必须用管理员权限启动
注意一定要用 netsh http delete urlacl 命令删除掉所有自己添加的urlacl,具体使用方法请谷歌
客户端注意要打开cn.etetet.loader/Resources/GlobalConfig, 把CodeMode换成Client
21. 注意独立运行服务器的目录不再是Bin目录,而是Bin的上一层目录,也就是Unity目录,比如 dotnet.exe Bin/ET.App.dll --Console=1。用rider启动默认是在Bin目录,需要自己修改运行目录,去掉Bin
22. 有问题请论坛提问,贴出服务端error log跟客户端error log,没有日志无法回复
# 打包过程
1. 去掉Platform Settings里的Development Build
2. 点击HybridCLR -> Installer,点击安装,等待安装完成
3. 右键编译ET.sln
4. 点击HybridCLR -> Generate -> All
5. 点击ET -> HybridCLR -> CopyAotDlls,这一步会把需要补充元数据的dll复制到'Assets/Bundles/AotDlls'目录
6. 打开YooAsset -> AssetBundle Builder窗口,按照以下步骤操作:
①BuildPipeline : '**ScriptableBuildPipeline**'
②BuildMode : '**IncrementalBuild**'
③CopyBuildinFileOption : '**ClearAndCopyAll**'
④点击'**Click Build**'
7. ET.YooAssets/Resources YooConfig中 EPlayMode选择'HostPlayMode', 打开Unity菜单 -> ET -> BuildTool,点击'BuildPackage',Windows下生成的exe在'ET/Release'里面
另:**请自行研究**YooAsset包管理库的使用方式([YooAsset官网](https://www.yooasset.com/))
# 热重载
1. 若需要体验此功能,需要在Unity菜单 -> Edit -> Preferences -> General窗口的 'ScriptChangesWhilePlaying' 中 选择 '**RecompileAfterFinishedPlaying**'
2. 运行后修改并编译代码,点击Unity菜单 -> ET -> Reload(或按快捷键**F7**)即可
# 注意事项:
一、常见出错原因:
1. 中文目录
2. Rider或VS没有更新到最新版本
3. Rider或VS没有安装相关组件
4. 没安装.Net8
5. 没编译服务端所有工程
6. Unity版本不兼容
7. Win7用户,没有特别设置
8. 如果打包报错缺少"StreamingAsset",自己在 'ET/Unity/Assets' 下新建一个 StreamingAsset 文件夹即可
================================================
FILE: Book/1.2Why use .net core.md
================================================
# Why use C# .net core for server-side?
Game server side from the early single service to distributed, the development is more and more complex, the stability, development efficiency requirements are more and more high. The choice of development language has also gradually changed, from C to C++ to C++ + PYTHON or C++ + LUA, and now many companies are using erlang, go, java, c#. At present, it is a blossoming situation.
But if you were to redo an online game server, without considering the compatibility with the company or what is already there, how would you choose? I thought carefully about this issue, there are probably several aspects of this need to consider: 1.
###### 1. Stability of the language (fatal)
The game server is characterized by high load and low latency. So generally the server-side process is with state, once hung means data loss, this is intolerable.
###### 2. Runtime hot more (fatal)
The game server logic is extremely complex and prone to bugs, but it can't be stopped often, so hot shifts to fix bugs are necessary. If a bug occurs, the developer can write code immediately and then fix it with a hot-change, which is not felt by online users at all.
###### 3. Availability of concurrent process support (5 stars importance)
With distributed server architecture, there is bound to be a lot of interaction between processes. Since it is difficult to split the game logic into multiple threads, it is generally single-threaded logic. If there is no concurrent support, a large number of callbacks are bound to be generated and code maintenance will become very difficult.
###### 4. Compilation speed (5 stars in importance)
In c++ development, 30% of the time is wasted on compilation. If compilation is fast or not needed, it will greatly improve development efficiency.
###### 5. Cross-platform (4 stars)
Generally the game server is set up on linux. But the usual development, the use of windows will be more convenient, if cross-platform, development and testing efficiency will be greatly improved, and do not need to get a separate development machine, the local computer can meet the usual development
###### 6. Readability, refactorability (3 stars)
The code can be reconstructed to greatly reduce the difficulty of writing code
###### 7. Whether the library is complete, whether the ecology is perfect (3 stars)
Library is complete, ecological good, you need to build their own wheel is less
###### 8. Unified language with the client (3 stars)
Client-server shared language, the advantages are very obvious, many codes can be reused, logic programmers no longer need to distinguish between the front and back end, both ends can write, a person can complete a function, greatly reducing the time cost of communication.
###### 9. IDE support (3 stars)
Code hints, refactoring and other support, excellent IDE can improve the development efficiency by several times.
###### 10. Language performance (1 star)
Currently server performance is not too much of a problem, but good performance is better than poor performance.
| Languages | C# | C/C++ | Java | Go | Lua | Python | Erlang |
| -- | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
| Stability | Stable | Easy to hang | Stable | Stable | Stable | Stable | Stable | Stable | Stable
| Runtime Hot Update | Support | Difficult to support | Support | Doesn't support | Support | Support | Support
| Cross-Platform | Support | Difficult to Support | Support | Difficult to Support | Support | Support | Support
| Coroutine | Have | Need to Implement | Bad Support | Support | Support | Support | Support
| Compilation speed | fast | slow | fast | fast | no compilation | no compilation | fast
| Readability | Good | Fair | Good | Fair | Poor | Poor | Poor|
| Game Library and Ecology | Good | Good | Average | Average | Poor | Good | Average|
| Client Unified Language | Unity | Unity, UE4 | Not available | Not available | Unity, UE4 | UE4 | Not available
| IDE Support | Good | Good | Good | Average | Poor | Poor | Poor|
| Language performance | Good | Very good | Good | Good | Poor | Very poor | Poor |
As you can see from the table. 1:
1. C/C++ has poor stability, slow compilation speed, and fatal flaws
2. Go does not support hot changes, does not support generics, poor reconfigurability, can not share code with the client, a fatal flaw
3. poor support for Java concurrency, unable to share code with the client
4. Lua has few libraries, poor performance, poor code readability and reconfigurability, cross-platform dependence on C/C++, troublesome to handle, and poor ide support
5. Python poor performance, poor code readability and reconfigurability, unable to share code with clients, poor ide support
6. Erlang performance is poor, functional style is not easy to get started, ide support is poor
7. C# .net core is very good in every convenience, but can't share code with UE4
Currently Unity is the hottest game engine, C# server with Unity is a perfect match, basically can not find defects.
================================================
FILE: Book/1.2为什么使用.net core.md
================================================
# 为什么使用C# .net core做服务端?
游戏服务端从早期的单服到分布式,开发越来越复杂,对稳定性,开发效率要求越来越高。开发语言的选择也逐步发生了变化,C 到 C++ 到 C++ + PYTHON 或者C++ + LUA 到现在 很多公司开始使用erlang,go,java,c#。目前是一个百花齐放的局面。
但是如果是要你重新做一个网游server,不考虑对公司或者已有的东西兼容性,你会怎么选择?我仔细想了一下这个问题,大概有这个几个方面需要考虑:
###### 1. 语言的稳定性(致命性)
游戏服务器的特点是高负载低延时。所以一般服务端进程都是带状态的,一旦挂掉就意味着数据丢失,这点是无法容忍的。
###### 2. 运行时热更(致命性)
游戏服务器逻辑极其复杂,很容易出现bug,但是又不能经常停服,所以热更修复bug就显得十分必要。出现错误开发人员可以立即编写代码,然后热更修复,线上用户完全感觉不到。
###### 3. 是否有协程支持(重要性5星)
分布式服务器架构,进程与进程之间必然会有大量交互。由于游戏逻辑很难拆分成多线程,所以一般来说都是逻辑单线程。如果没有协程支持,必然产生大量回调,代码维护会变得非常困难。
###### 4. 编译速度(重要性5星)
使用c++开发,30%的时间都浪费在编译上。假如编译很快或者不需要编译,必定大大提高开发效率。
###### 5. 跨平台(4星)
一般游戏服务器都架设在linux上面。但是平常开发,使用windows会更加方便,如果跨平台,开发以及测试效率会大大提升,并且不需要单独搞一个开发机,本机电脑就可以满足平常开发
###### 6. 可阅读性,可重构性(3星)
代码可以重构能大大减轻写代码的难度
###### 7. 库是否齐全,生态是否完善(3星)
库齐全,生态好,自己需要造的轮子就少
###### 8.跟客户端统一语言(3星)
客户端服务端共用语言,优势十分明显,很多代码可以复用,逻辑程序员不再需要区分前后端,双端都可以写,一个人即可完成一个功能,大大减少了沟通的时间成本。
###### 9. IDE的支持(3星)
代码提示,重构等支持,优秀的IDE能提高几倍的开发效率。
###### 10. 语言的性能(1星)
目前服务器性能都不是太大问题,不过性能好总比性能差要强。
| 语言 | C# | C/C++ | Java | Go | Lua | Python | Erlang |
| -- | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
| 稳定性 | 稳定 | 容易挂 | 稳定 | 稳定 | 稳定 | 稳定 | 稳定 |
| 运行时热更 | 支持 | 较难支持 | 支持 | 不支持 | 支持 | 支持 | 支持 |
| 跨平台 | 支持 | 较难支持 | 支持 | 支持 | 较难支持 | 支持 | 支持 |
| 协程 | 有 | 需要自己实现 | 支持不好 | 支持 | 支持 | 支持 | 支持 |
| 编译速度 | 快 | 慢 | 快 | 快 | 不需要编译 | 不需要编译 | 快|
| 阅读性重构性 | 好 | 一般 | 好 | 一般 | 差 | 差 | 差|
| 游戏库跟生态 | 好 | 好 | 一般 | 一般 | 差 | 好 | 一般|
| 客户端统一语言 | Unity | Unity、UE4 | 暂无 | 暂无 | Unity、UE4 | UE4 | 暂无|
| IDE的支持 | 好 | 好 |好 | 普通| 差 | 差 | 差|
| 语言的性能 | 好|极好| 好| 好| 差|很差|差|
从表格可以看出:
1. C/C++稳定性差,编译速度慢,存在致命缺陷
2. Go不支持热更,由于不支持泛型,重构性较差,无法跟客户端共享代码,存在致命缺陷
3. Java协程支持差,无法跟客户端共享代码
4. Lua库少,性能差,代码可阅读性可重构性差,跨平台完全依赖C/C++,处理起来麻烦,ide支持差
5. Python 性能很差,代码可阅读性可重构性差,无法跟客户端共享代码,ide支持差
6. Erlang 性能差,函数式风格不好上手,ide支持差
7. C# .net core各个方便都非常优秀,不过跟UE4无法共享代码
当前Unity是最火的游戏引擎,C#服务端搭配Unity完全是天作之合,基本上找不到缺陷。
================================================
FILE: Book/2.1CSharp Coroutine.md
================================================
# What is a concurrent thread
Speaking of concurrency, let's first understand what is asynchronous. Asynchronous simply means that I want to initiate a call, but the called party (may be other threads, or may be IO) will take some time to produce the result, and I don't want the call to block the entire thread of the caller, so I pass a callback function to the called party, and the called party will call back this callback function after it finishes running to notify the caller to continue The callback function will notify the caller to continue execution. As an example:
In the following code, the main thread keeps looping, sleep 1 millisecond per loop, add one to the count, and print once every 10,000 times.
```csharp
private static void Main()
{
int loopCount = 0;
while (true)
{
int temp = watcherValue;
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
```
At this point I need to add a function, at the beginning of the program, I want to print the value of loopCount after 5 seconds. Seeing that after 5 seconds we can think of the Sleep method, which will block the thread for a certain amount of time and then continue execution. We obviously can't Sleep in the main thread because it would break the logic of printing once every 10000 counts.
```csharp
// example2_1
class Program
{
private static int loopCount = 0;
private static void Main()
{
OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
WaitTimeAsync(5000, WaitTimeFinishCallback);
while (true)
{
OneThreadSynchronizationContext.Instance.Update();
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static void WaitTimeAsync(int waitTime, Action action)
{
Thread thread = new Thread(()=>WaitTime(waitTime, action));
thread.Start();
}
private static void WaitTimeFinishCallback()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
}
/// <summary>
/// Waiting in another thread
/// </summary>
private static void WaitTime(int waitTime, Action action)
{
Thread.Sleep(waitTime);
// Throw the action back to the main thread for execution
OneThreadSynchronizationContext.Instance.Post((o)=>action(), null);
}
}
```
We have designed a WaitTimeAsync method here. WaitTimeAsync is actually a typical asynchronous method that initiates a call from the main thread, passes in a WaitTimeFinishCallback method as an argument, opens a thread, and after the thread Sleeps for a certain amount of time, throws the passed callback back to the main thread for execution. OneThreadSynchronizationContext is a cross-thread queue, any thread can throw delegates into it, the Update method of OneThreadSynchronizationContext is called in the main thread and will take out these delegates and put them into the main thread for execution. Why does the callback method need to be thrown back to the main thread for execution? Because the loopCount is read in the callback method, and the loopCount is also read and written in the main thread, so either add a lock or always ensure that it is only read and written in the main thread. It is a bad practice to add locks, and having locks all over the code makes it difficult to read and maintain, and it is easy to create multi-threaded bugs. this is a common technique in multi-threaded development to package the logic into a delegate and throw it back to another thread.
We may have a new requirement, after the execution of WaitTimeFinishCallback is finished, we want to wait for 3 seconds and print the loopCount again.
```csharp
private static void WaitTimeAsync(int waitTime, Action action)
{
Thread thread = new Thread(()=>WaitTime(waitTime, action));
thread.Start();
}
private static void WaitTimeFinishCallback()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
WaitTimeAsync(3000, WaitTimeFinishCallback2);
}
private static void WaitTimeFinishCallback2()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
}
```
We may also change the requirement at this point, we need to print the loopCount 5 seconds after the program starts, then 4 seconds after, then 3 seconds after, that is, insert another 3 seconds wait in the middle of the above logic.
```csharp
private static void WaitTimeAsync(int waitTime, Action action)
{
Thread thread = new Thread(()=>WaitTime(waitTime, action));
thread.Start();
}
private static void WaitTimeFinishCallback()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
WaitTimeAsync(4000, WaitTimeFinishCallback3);
}
private static void WaitTimeFinishCallback3()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
WaitTimeAsync(3000, WaitTimeFinishCallback2);
}
private static void WaitTimeFinishCallback2()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
}
```
This inserts a piece of code in the middle, which seems very cumbersome. Here you can answer what is a concurrent process, in fact this string of callbacks is a concurrent process.
================================================
FILE: Book/2.1CSharp的协程.md
================================================
# 什么是协程
说到协程,我们先了解什么是异步,异步简单说来就是,我要发起一个调用,但是这个被调用方(可能是其它线程,也可能是IO)出结果需要一段时间,我不想让这个调用阻塞住调用方的整个线程,因此传给被调用方一个回调函数,被调用方运行完成后回调这个回调函数就能通知调用方继续往下执行。举个例子:
下面的代码,主线程一直循环,每循环一次sleep 1毫秒,计数加一,每10000次打印一次。
```csharp
private static void Main()
{
int loopCount = 0;
while (true)
{
int temp = watcherValue;
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
```
这时我需要加个功能,在程序一开始,我希望在5秒钟之后打印出loopCount的值。看到5秒后我们可以想到Sleep方法,它会阻塞线程一定时间然后继续执行。我们显然不能在主线程中Sleep,因为会破坏掉每10000次计数打印一次的逻辑。
```csharp
// example2_1
class Program
{
private static int loopCount = 0;
private static void Main()
{
OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
WaitTimeAsync(5000, WaitTimeFinishCallback);
while (true)
{
OneThreadSynchronizationContext.Instance.Update();
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static void WaitTimeAsync(int waitTime, Action action)
{
Thread thread = new Thread(()=>WaitTime(waitTime, action));
thread.Start();
}
private static void WaitTimeFinishCallback()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
}
/// <summary>
/// 在另外的线程等待
/// </summary>
private static void WaitTime(int waitTime, Action action)
{
Thread.Sleep(waitTime);
// 将action扔回主线程执行
OneThreadSynchronizationContext.Instance.Post((o)=>action(), null);
}
}
```
我们在这里设计了一个WaitTimeAsync方法,WaitTimeAsync其实就是一个典型的异步方法,它从主线程发起调用,传入了一个WaitTimeFinishCallback回调方法做参数,开启了一个线程,线程Sleep一定时间后,将传过来的回调扔回到主线程执行。OneThreadSynchronizationContext是一个跨线程队列,任何线程可以往里面扔委托,OneThreadSynchronizationContext的Update方法在主线程中调用,会将这些委托取出来放到主线程执行。为什么回调方法需要扔回到主线程执行呢?因为回调方法中读取了loopCount,loopCount在主线程中也有读写,所以要么加锁,要么永远保证只在主线程中读写。加锁是个不好的做法,代码中到处是锁会导致阅读跟维护困难,很容易产生多线程bug。这种将逻辑打包成委托然后扔回另外一个线程是多线程开发中常用的技巧。
我们可能又有个新需求,WaitTimeFinishCallback执行完成之后,再想等3秒,再打印一下loopCount。
```csharp
private static void WaitTimeAsync(int waitTime, Action action)
{
Thread thread = new Thread(()=>WaitTime(waitTime, action));
thread.Start();
}
private static void WaitTimeFinishCallback()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
WaitTimeAsync(3000, WaitTimeFinishCallback2);
}
private static void WaitTimeFinishCallback2()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
}
```
我们这时还可能改需求,需要在程序启动5秒后,接下来4秒,再接下来3秒,打印loopCount,也就是上面的逻辑中间再插入一个3秒等待。
```csharp
private static void WaitTimeAsync(int waitTime, Action action)
{
Thread thread = new Thread(()=>WaitTime(waitTime, action));
thread.Start();
}
private static void WaitTimeFinishCallback()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
WaitTimeAsync(4000, WaitTimeFinishCallback3);
}
private static void WaitTimeFinishCallback3()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
WaitTimeAsync(3000, WaitTimeFinishCallback2);
}
private static void WaitTimeFinishCallback2()
{
Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
}
```
这样中间插入一段代码,显得非常麻烦。这里可以回答什么是协程了,其实这一串串回调就是协程。
================================================
FILE: Book/2.2Better Coroutine.md
================================================
# Better Coroutine
The above article talks about how a string of callbacks is a concurrent process, and obviously writing code this way, adding logic and inserting logic is very error prone. We need to use asynchronous syntax to change the form of this asynchronous callback to a synchronous form, fortunately C# has been designed for us, see the code
```csharp
// example2_2
class Program
{
private static int loopCount = 0;
static void Main(string[] args)
{
OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
Console.WriteLine($"Main Thread: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
OneThreadSynchronizationContext.Instance.Update();
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static async void Crontine()
{
await WaitTimeAsync(5000);
Console.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount's value is: {loopCount}");
await WaitTimeAsync(4000);
Console.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih The value of loopCount is: {loopCount}");
await WaitTimeAsync(3000);
Console.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih The value of loopCount is: {loopCount}");
}
private static Task WaitTimeAsync(int waitTime)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Thread thread = new Thread(()=>WaitTime(waitTime, tcs));
thread.Start();
return tcs.Task;
}
/// <summary>
/// Waiting in another thread
/// </summary>
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
{
Thread.Sleep(waitTime);
// throw tcs back to the main thread for execution
OneThreadSynchronizationContext.Instance.Post(o=>tcs.SetResult(true), null);
}
}
```
In this code, in the WaitTimeAsync method, we use the TaskCompletionSource class instead of the previously passed Action parameter, and the WaitTimeAsync method returns a Task type result. waitTime we replace action() with tcs. SetResult(true), and the WaitTimeAsync method uses the await keyword in front of it, so that the sequence of callbacks can be changed to a synchronized form. This makes the code look very simple and much easier to develop.
Here is another trick, we found that WaitTime needs to throw tcs.SetResult back to the main thread for execution, Microsoft gives us a simple way to set up the synchronization context in the main thread by referring to example2_2_2
```csharp
// example2_2_2
SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);
```
SetResult(true) will be called directly in WaitTime, the callback will be automatically thrown to the synchronization context, and the synchronization context we can take out in the main thread to execute the callback, so automatically able to complete the operation back to the main thread
```csharp
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
{
Thread.Sleep(waitTime);
tcs.SetResult(true);
}
```
If you do not set the synchronization context, you will find that the printout of the current thread is not the main thread, which is also the use of many third-party libraries and . In fact, I think this design is not necessary, to the library developers to achieve better, especially in the game development, logic is all single-threaded, callback every time you go through the synchronization context is redundant, so the ET framework provides the implementation of ETTask does not use the synchronization context, the code is more concise and efficient, which will be discussed later.
================================================
FILE: Book/2.2更好的协程.md
================================================
# 更好的协程
上文讲了一串回调就是协程,显然这样写代码,增加逻辑,插入逻辑非常容易出错。我们需要利用异步语法把这个异步回调的形式改成同步的形式,幸好C#已经帮我们设计好了,看代码
```csharp
// example2_2
class Program
{
private static int loopCount = 0;
static void Main(string[] args)
{
OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
OneThreadSynchronizationContext.Instance.Update();
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static async void Crontine()
{
await WaitTimeAsync(5000);
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
await WaitTimeAsync(4000);
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
await WaitTimeAsync(3000);
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
}
private static Task WaitTimeAsync(int waitTime)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Thread thread = new Thread(()=>WaitTime(waitTime, tcs));
thread.Start();
return tcs.Task;
}
/// <summary>
/// 在另外的线程等待
/// </summary>
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
{
Thread.Sleep(waitTime);
// 将tcs扔回主线程执行
OneThreadSynchronizationContext.Instance.Post(o=>tcs.SetResult(true), null);
}
}
```
在这段代码里面,WaitTimeAsync方法中,我们利用了TaskCompletionSource类替代了之前传入的Action参数,WaitTimeAsync方法返回了一个Task类型的结果。WaitTime中我们把action()替换成了tcs.SetResult(true),WaitTimeAsync方法前使用await关键字,这样可以将一连串的回调改成同步的形式。这样一来代码显得十分简洁,开发起来也方便多了。
这里还有个技巧,我们发现WaitTime中需要将tcs.SetResult扔回到主线程执行,微软给我们提供了一种简单的方法,参考example2_2_2,在主线程设置好同步上下文,
```csharp
// example2_2_2
SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);
```
在WaitTime中直接调用tcs.SetResult(true)就行了,回调会自动扔到同步上下文中,而同步上下文我们可以在主线程中取出回调执行,这样自动能够完成回到主线程的操作
```csharp
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
{
Thread.Sleep(waitTime);
tcs.SetResult(true);
}
```
如果不设置同步上下文,你会发现打印出来当前线程就不是主线程了,这也是很多第三方库跟.net core内置库的用法,默认不回调到主线程,所以我们使用的时候需要设置下同步上下文。其实这个设计本人觉得没有必要,交由库的开发者去实现更好,尤其是在游戏开发中,逻辑全部是单线程的,回调每次都走一遍同步上下文就显得多余了,所以ET框架提供了不使用同步上下文的实现ETTask,代码更加简洁更加高效,这个后面会讲到。
================================================
FILE: Book/2.3Single-threaded asynchronous.md
================================================
# Single-threaded asynchronous
The previous examples are multi-threaded implementations of asynchrony, but asynchrony is obviously not just multi-threaded. We used Sleep in the previous examples to achieve time waiting, each timer needs to use a thread, which will lead to frequent thread switching, this implementation is very inefficient, usually will not do so. General game logic will design a single-threaded timer, we do a simple implementation here, used to explain single-threaded asynchronous.
```csharp
// example2_3
class Program
{
private static int loopCount = 0;
private static long time;
private static Action action;
static void Main(string[] args)
{
Console.WriteLine($"Main thread: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
Thread.Sleep(1);
CheckTimerOut();
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static void Crontine()
{
WaitTimeAsync(5000, WaitTimeAsyncCallback1);
}
private static void WaitTimeAsyncCallback1()
{
Console.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount's value is: {loopCount}");
WaitTimeAsync(4000, WaitTimeAsyncCallback2);
}
private static void WaitTimeAsyncCallback2()
{
Console.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount's value is: {loopCount}");
WaitTimeAsync(3000, WaitTimeAsyncCallback3);
}
private static void WaitTimeAsyncCallback3()
{
Console.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount's value is: {loopCount}");
}
private static void CheckTimerOut()
{
if (time == 0)
{
return;
}
long nowTicks = DateTime.Now.Ticks / 10000;
if (time > nowTicks)
{ return; }
return;
}
time = 0;
action.Invoke();
}
private static void WaitTimeAsync(int waitTime, Action a)
{
Ticks / 10000 + waitTime;
action = a;
}
}
```
This example also implements a simple timing method, WaitTimeAsync will be called to record the callback method and time, the main thread will call CheckTimerOut every frame, CheckTimerOut inside to determine whether the timer is expired, expired then callback method is called. The whole logic is done in the main thread, also asynchronously. So asynchronous is not multi-threaded, single-threaded can also be asynchronous. The above example can be changed to await as well.
```csharp
// example2_3_2
class Program
{
private static int loopCount = 0;
private static long time;
private static TaskCompletionSource<bool> tcs;
static void Main(string[] args)
{
Console.WriteLine($"Main thread: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
Thread.Sleep(1);
CheckTimerOut();
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static async void Crontine()
{
await WaitTimeAsync(5000);
Console.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount's value is: {loopCount}");
await WaitTimeAsync(4000);
Console.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih The value of loopCount is: {loopCount}");
await WaitTimeAsync(3000);
Console.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih The value of loopCount is: {loopCount}");
}
private static void CheckTimerOut()
{
if (time == 0)
{
return;
}
long nowTicks = DateTime.Now.Ticks / 10000;
if (time > nowTicks)
{ return; }
return;
}
time = 0;
tcs.SetResult(true);
}
private static Task WaitTimeAsync(int waitTime)
{
TaskCompletionSource<bool> t = new TaskCompletionSource<bool>();
Time = DateTime.Now.Ticks / 10000 + waitTime;
tcs = t;
return t.Task;
}
}
```
The above example all calls are done in the main thread and use await, so await does not open multithreading, await specific use of multithreading depends entirely on the specific implementation
================================================
FILE: Book/2.3单线程异步.md
================================================
# 单线程异步
前面几个例子都是多线程实现的异步,但是异步显然不仅仅是多线程的。我们在之前的例子中使用了Sleep来实现时间的等待,每一个计时器都需要使用一个线程,会导致线程切换频繁,这个实现效率很低,平常是不会这样做的。一般游戏逻辑中会设计一个单线程的计时器,我们这里做一个简单的实现,用来讲解单线程异步。
```csharp
// example2_3
class Program
{
private static int loopCount = 0;
private static long time;
private static Action action;
static void Main(string[] args)
{
Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
Thread.Sleep(1);
CheckTimerOut();
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static void Crontine()
{
WaitTimeAsync(5000, WaitTimeAsyncCallback1);
}
private static void WaitTimeAsyncCallback1()
{
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
WaitTimeAsync(4000, WaitTimeAsyncCallback2);
}
private static void WaitTimeAsyncCallback2()
{
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
WaitTimeAsync(3000, WaitTimeAsyncCallback3);
}
private static void WaitTimeAsyncCallback3()
{
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
}
private static void CheckTimerOut()
{
if (time == 0)
{
return;
}
long nowTicks = DateTime.Now.Ticks / 10000;
if (time > nowTicks)
{
return;
}
time = 0;
action.Invoke();
}
private static void WaitTimeAsync(int waitTime, Action a)
{
time = DateTime.Now.Ticks / 10000 + waitTime;
action = a;
}
}
```
这个例子同样实现了一个简单的计时方法,WaitTimeAsync调用时会将回调方法跟时间记录下来,主线程每帧都会调用CheckTimerOut,CheckTimerOut里面判断计时器是否过期,过期了则调用回调方法。整个逻辑都在主线程中完成,同样是异步方法。所以异步并非多线程,单线程同样可以异步。上面的例子同样可以改成await的形式。
```csharp
// example2_3_2
class Program
{
private static int loopCount = 0;
private static long time;
private static TaskCompletionSource<bool> tcs;
static void Main(string[] args)
{
Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
Thread.Sleep(1);
CheckTimerOut();
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static async void Crontine()
{
await WaitTimeAsync(5000);
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
await WaitTimeAsync(4000);
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
await WaitTimeAsync(3000);
Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
}
private static void CheckTimerOut()
{
if (time == 0)
{
return;
}
long nowTicks = DateTime.Now.Ticks / 10000;
if (time > nowTicks)
{
return;
}
time = 0;
tcs.SetResult(true);
}
private static Task WaitTimeAsync(int waitTime)
{
TaskCompletionSource<bool> t = new TaskCompletionSource<bool>();
time = DateTime.Now.Ticks / 10000 + waitTime;
tcs = t;
return t.Task;
}
}
```
上面这个例子所有调用全部在主线程中完成,并且使用了await,因此await并不会开启多线程,await具体用没用多线程完全取决于具体的实现
================================================
FILE: Book/3.2The powerful MongoBson library.md
================================================
# The powerful MongoBson library
Back-end development, statistics about these scenarios need to use serialization: 1.
1. object clone by serialization deserialization
2. server-side database storage data, binary
3. distributed server-side, multi-process messages, binary
4. back-end logs, text format
5. various server-side configuration files, text format
There are very, very many C# serialization libraries, protobuf, json and so on. But these serialization libraries should not be readable and small for all scenarios. protobuf does not support complex object structures (cannot use inheritance) and is suitable for messages, but not for database storage and log formats. json is suitable for log formats, but is too big for network messages and data storage. We certainly want a library that can satisfy all the above scenarios for the following reasons.
1. you think about one day your configuration file needs to be saved in the database, you do not need to do the format conversion, the back-end directly to the configuration message sent by the front-end to save to the database, which can not reduce very many errors?
2. one day some server-side configuration files do not use the file format, need to be placed in the database, again, only a few lines of code to complete the migration.
3. one day the back-end server crash, you need to scan the logs for data recovery, the logs for deserialization into C# objects, one by one for processing, and then into objects saved to the database is complete.
4. objects saved in the database, you can directly see the text content, you can do a variety of SQL-like operations
5. Imagine a scenario where a configuration text object, deserialized into memory, sent via network messages, and stored in the database. The whole process is done in one go.
Simply put, it reduces various data conversions, reduces code, improves development efficiency, and improves maintainability. MongoDB library can serialize both text and BSON binary format, and MongoDB itself is a very much used database in the game. Its support features are as follows.
1. support complex inheritance structure
2. support for ignoring certain fields serialization
3. support field default values
4. structure with extra fields can still be deserialized, which is very useful for multi-version protocols
5. support for ISupportInitialize interface, this is a godsend when deserialization
6. support for text json and binary bson serialization
7. MongoDB database support
A brief introduction to the mongo bson library
### 1. Support serialization deserialization into json or bson
```csharp
public sealed class Player
{
public long Id;
public string Account { get; private set; }
public long UnitId { get; set; }
}
Player player1 = new Player() { Id = 1 };
string json = player1.ToJson();
Console.WriteLine($"player1 to json: {json}");
Console.WriteLine($"player to bson: {player.ToBson().ToHex()}");
// output:
// player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
// player to bson: B000000125F69640001000000000000000A4163636F756E740012556E69744964000000000000000000000000
```
Note that mongo's json is a bit different from the standard json, if you want to use the standard json, you can pass in a JsonWriterSettings object and restrict the use of JsonOutputMode.Strict mode
```csharp
// use standard json
Player player2 = new Player() { Id = 1 };
Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
// player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
```
Deserialize json:
```csharp
// deserialize json
Player player11 = BsonSerializer.Deserialize<Player>(json);
Console.WriteLine($"player11 to json: {player11.ToJson()}");
```
Deserialize bson:
```csharp
// deserialize bson
using (MemoryStream memoryStream = new MemoryStream(bson))
{
Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
Console.WriteLine($"player12 to json: {player12.ToJson()}");
}
```
### 2. Some fields can be ignored
[BsonIgnore] This tag is used to disable field serialization.
```csharp
public sealed class Player
{
public long Id;
[BsonIgnore]
public string Account { get; private set; }
public long UnitId { get; set; }
}
Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
Console.WriteLine($"player to json: {player.ToJson()}");
// player to json: { "_id" : 2, "UnitId" : 3 }
```
### 3. Support for default values and taking aliases
The [BsonElement] field with this tag will serialize even private fields (only public fields are serialized by default), and the tag can take a string parameter to assign an alias to the field serialization.
```csharp
public sealed class Player
{
public long Id;
public string Account { get; private set; }
[BsonElement("UId")]
public long UnitId { get; set; }
}
Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
Console.WriteLine($"player to json: {player.ToJson()}");
// player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }
```
### 4. Upgrade version support
[BsonIgnoreExtraElements] This tag is used on top of class, used to ignore extra fields when deserializing, general version compatibility needs to be considered, low version of the protocol needs to be able to deserialize
otherwise the new version adds fields, the old version structure deserialization will be wrong
```csharp
[BsonIgnoreExtraElements]
public sealed class Player
public
public long Id;
public string Account { get; private set; }
[BsonElement("UId")
public long UnitId { get; set; }
}
```
### 5. Support for complex inheritance structures
The power of the mongo bson library is that it fully supports serialization deserialization inheritance structures. Note that inheritance deserialization requires registration of all parent classes, and there are two ways to do this.
a. You can declare the inherited subclasses on top of the parent class using the [BsonKnownTypes] tag, so that mongo will automatically register them, e.g.:
```csharp
[BsonKnownTypes(typeof(Entity))]
public class Component
{
}
[BsonKnownTypes(typeof(Player))]
public class Entity: Component
{
}
public sealed class Player: Entity
{
public long Id;
public string Account { get; set; }
public long UnitId { get; set; }
}
```
This is flawed because the framework doesn't know what subclasses a class will have, and this is invasive to the framework code, and we want to uncouple this
. You can scan the assembly for the types of all subclass parents and register them with the mongo driver
```csharp
Type[] types = typeof(Game).Assembly.GetTypes();
foreach (Type type in types)
{
if (!type.IsSubclassOf(typeof(Component))))
{
continue;
}
BsonClassMap.LookupClassMap(type);
}
BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));
```
This completely automates the registration and the user does not need to relate whether the class is registered or not.
### 6. ISupportInitialize interface
mongo bson deserialization supports an ISupportInitialize interface, ISupportInitialize has two methods
```csharp
public interface ISupportInitialize
{
void BeginInit();
void EndInit();
}
```
BeginInit is called before deserialization and EndInit is called after deserialization. This interface is very useful now to perform some operations after deserialization. For example
```csharp
[BsonIgnoreExtraElements]
public class InnerConfig: AConfigComponent
{
[BsonIgnore]
public IPEndPoint IPEndPoint { get; private set; }
public string Address { get; set; }
public override void EndInit()
{
this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address);
}
}
````
InnerConfig is the configuration of the process inner network address in ET. Since IPEndPoint is not very configurable, we can configure it as a string and then convert the string to IPEndPoint in EndInit when deserializing.
I also added this call to the protobuf deserialization method, refer to ProtobufHelper.cs, ET's protobuf because to support ilruntime, so remove the map support, if we want a map how to do? Here I gave the generated code are done, the proto messages are changed to a partial class, so that we can extend the class themselves, for example.
```csharp
message UnitInfo
{
int64 UnitId = 1;
float X = 2;
float Y = 3;
float Z = 4;
}
// protobuf
message G2C_EnterMap // IResponse
{
int32 RpcId = 90;
int32 Error = 91;
string Message = 92;
// own unit id
int64 UnitId = 1;
// all the units
repeated UnitInfo Units = 2;
}
```
This network message has a repeated UnitInfo field, which is actually an array in protobuf, which is not very convenient to use, I want to convert it to a Dictionary<Int64, UnitInfo> field, we can do something like this
```csharp
public partial class G2C_EnterMap: ISupportInitialize
{
public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>();
public void BeginInit()
{
}
public void EndInit()
{
foreach (var unit in this.Units)
{
this.unitsDict.Add(unit.UnitId, unit);
}
}
}
```
The message is extended by such a piece of code, and after deserialization, it is automatically converted into a Dictionary.
================================================
FILE: Book/3.2强大的MongoBson库.md
================================================
# 强大的MongoBson库
后端开发,统计了一下大概有这些场景需要用到序列化:
1. 对象通过序列化反序列化clone
2. 服务端数据库存储数据,二进制
3. 分布式服务端,多进程间的消息,二进制
4. 后端日志,文本格式
5. 服务端的各种配置文件,文本格式
C#序列化库有非常非常多了,protobuf,json等等。但是这些序列化库都无法应当所有场景,既要可读又要小。protobuf不支持复杂的对象结构(无法使用继承),做消息合适,做数据库存储和日志格式并不好用。json做日志格式合适,但是做网络消息和数据存储就太大。我们当然希望一个库能满足上面所有场景,理由如下:
1. 你想想某天你的配置文件需要放到数据库中保存,你不需要进行格式转换,后端直接把前端发过来的配置消息保存到数据库中,这是不是能减少非常多错误呢?
2. 某天有些服务端的配置文件不用文件格式了,需要放在数据库中,同样,只需要几行代码就可以完成迁移。
3. 某天后端服务器crash,你需要扫描日志进行数据恢复,把日志进行反序列化成C#对象,一条条进行处理,再转成对象保存到数据库就完成了。
4. 对象保存在数据库,直接就可以看到文本内容,可以做各种类sql的操作
5. 想像一个场景,一个配置文本对象,反序列化到内存,通过网络消息发送,存储到数据库中。整个过程一气呵成。
简单来说就是减少各种数据转换,减少代码,提高开发效率,提高可维护性。当然,Mongo Bson就能够满足。MongoDB库既可以序列化成文本也可以序列化成BSON的二进制格式,并且MongoDB本身就是一个游戏中使用非常多的数据库。Mongo Bson非常完善,是我见过功能最全使用最强大的序列化库,有些功能十分贴心。其支持功能如下:
1. 支持复杂的继承结构
2. 支持忽略某些字段序列化
3. 支持字段默认值
4. 结构多出多余的字段照样可以反序列化,这对多版本协议非常有用
5. 支持ISupportInitialize接口使用,这个在反序列化的时候简直就是神器
6. 支持文本json和二进制bson序列化
7. MongoDB数据库支持
简单的介绍下mongo bson库
### 1.支持序列化反序列化成json或者bson
```csharp
public sealed class Player
{
public long Id;
public string Account { get; private set; }
public long UnitId { get; set; }
}
Player player1 = new Player() { Id = 1 };
string json = player1.ToJson();
Console.WriteLine($"player1 to json: {json}");
Console.WriteLine($"player to bson: {player.ToBson().ToHex()}");
// output:
// player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
// player to bson: B000000125F69640001000000000000000A4163636F756E740012556E6974496400000000000000000000
```
注意mongo的json跟标准的json有点区别,如果想用标准的json,可以传入一个JsonWriterSettings对象,限制使用JsonOutputMode.Strict模式
```csharp
// 使用标准json
Player player2 = new Player() { Id = 1 };
Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
// player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
```
反序列化json:
```csharp
// 反序列化json
Player player11 = BsonSerializer.Deserialize<Player>(json);
Console.WriteLine($"player11 to json: {player11.ToJson()}");
```
反序列化bson:
```csharp
// 反序列化bson
using (MemoryStream memoryStream = new MemoryStream(bson))
{
Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
Console.WriteLine($"player12 to json: {player12.ToJson()}");
}
```
### 2.可以忽略某些字段
[BsonIgnore]该标签用来禁止字段序列化。
```csharp
public sealed class Player
{
public long Id;
[BsonIgnore]
public string Account { get; private set; }
public long UnitId { get; set; }
}
Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
Console.WriteLine($"player to json: {player.ToJson()}");
// player to json: { "_id" : 2, "UnitId" : 3 }
```
### 3.支持默认值以及取别名
[BsonElement] 字段加上该标签,即使是private字段也会序列化(默认只序列化public字段),该标签还可以带一个string参数,给字段序列化指定别名。
```csharp
public sealed class Player
{
public long Id;
public string Account { get; private set; }
[BsonElement("UId")]
public long UnitId { get; set; }
}
Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
Console.WriteLine($"player to json: {player.ToJson()}");
// player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }
```
### 4.升级版本支持
[BsonIgnoreExtraElements] 该标签用在class上面,反序列化时用来忽略多余的字段,一般版本兼容需要考虑,低版本的协议需要能够反
序列化高版本的内容,否则新版本加了字段,旧版本结构反序列化会出错
```csharp
[BsonIgnoreExtraElements]
public sealed class Player
{
public long Id;
public string Account { get; private set; }
[BsonElement("UId")]
public long UnitId { get; set; }
}
```
### 5.支持复杂的继承结构
mongo bson库强大的地方在于完全支持序列化反序列化继承结构。需要注意的是,继承反序列化需要注册所有的父类,有两种方法:
a. 你可以在父类上面使用[BsonKnownTypes]标签声明继承的子类,这样mongo会自动注册,例如:
```csharp
[BsonKnownTypes(typeof(Entity))]
public class Component
{
}
[BsonKnownTypes(typeof(Player))]
public class Entity: Component
{
}
public sealed class Player: Entity
{
public long Id;
public string Account { get; set; }
public long UnitId { get; set; }
}
```
这样有缺陷,因为框架并不知道一个类会有哪些子类,这样做对框架代码有侵入性,我们希望能解除这个耦合
。可以扫描程序集中所有子类父类的类型,将他们注册到mongo驱动中
```csharp
Type[] types = typeof(Game).Assembly.GetTypes();
foreach (Type type in types)
{
if (!type.IsSubclassOf(typeof(Component)))
{
continue;
}
BsonClassMap.LookupClassMap(type);
}
BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));
```
这样完全的自动化注册,使用者也不需要关心类是否注册。
### 6.ISupportInitialize接口
mongo bson反序列化时支持一个ISupportInitialize接口,ISupportInitialize有两个方法
```csharp
public interface ISupportInitialize
{
void BeginInit();
void EndInit();
}
```
BeginInit在反序列化前调用,EndInit在反序列化后调用。这个接口非常有用了,可以在反序列化后执行一些操作。例如
```csharp
[BsonIgnoreExtraElements]
public class InnerConfig: AConfigComponent
{
[BsonIgnore]
public IPEndPoint IPEndPoint { get; private set; }
public string Address { get; set; }
public override void EndInit()
{
this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address);
}
}
```
InnerConfig是ET中进程内网地址的配置,由于IPEndPoint不太好配置,我们可以配置成string形式,然后反序列化的时候在EndInit中把string转换成IPEndPoint。
同样我给protobuf反序列化方法也加上了这个调用,参考ProtobufHelper.cs,ET的protobuf因为要支持ilruntime,所以去掉了map的支持,假如我们想要一个map怎么办呢?这里我给生成的代码都做了手脚,把proto消息都改成了partial class,这样我们可以自己扩展这个class,比如:
```csharp
message UnitInfo
{
int64 UnitId = 1;
float X = 2;
float Y = 3;
float Z = 4;
}
// protobuf
message G2C_EnterMap // IResponse
{
int32 RpcId = 90;
int32 Error = 91;
string Message = 92;
// 自己的unit id
int64 UnitId = 1;
// 所有的unit
repeated UnitInfo Units = 2;
}
```
这个网络消息有个repeated UnitInfo字段,在protobuf中其实是个数组,使用起来不是很方便,我希望转成一个Dictionary<Int64, UnitInfo>的字段,我们可以做这样的操作:
```csharp
public partial class G2C_EnterMap: ISupportInitialize
{
public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>();
public void BeginInit()
{
}
public void EndInit()
{
foreach (var unit in this.Units)
{
this.unitsDict.Add(unit.UnitId, unit);
}
}
}
```
通过这样一段代码把消息进行扩展一下,反序列化出来之后,自动转成了一个Dictionary。
================================================
FILE: Book/3.3Everything is Entity.md
================================================
# Everything is Entity
The ECS design is very popular right now, mainly because of the success of Watchtower, which has led to the explosion of this technology. The most important design of ECS is the complete separation of logic and data. That is, EC is pure data, System is actually logic, by data-driven logic. What does data-driven logic mean? It is very simple to detect data changes through Update and subscribe to data changes through the event mechanism, which is called data-driven. Other features such as cache hits are not too important in writing logic, modern games are scripted, even the performance of the script can tolerate how will care about cache hits that performance improvement?
For example, in order to reuse, the data must be split into very small particles, which will lead to a very large number of components. But the game is developed cooperatively by multiple people, each person is basically only familiar with their own modules, which may end up causing a large number of redundant components. Another problem is that the common ECS is flat, with only one layer of Entity and Component. It is like a company, the biggest is the boss, the boss with hundreds of people under him, the boss can not know all the people, to complete a task, the boss can not pick out the people they need. A reasonable approach is to have several managers under the boss, several supervisors under each manager, and several workers under each supervisor, so as to form a tree-like management structure that is easy to manage. This is similar to the ET approach, where Entity can manage Component, Component can manage Entity, and even Component can mount Component. e.g. a person is composed of head, body, hands, feet, and the head is composed of eyes, ears, nose, mouth.
```csharp
Head head = human.AddComponent<Head>();
head.AddComponent<Eye>();
head.AddComponent<Mouse>();
AddComponent<Nose>();
AddComponent<Ear>();
human.AddComponent<Body>();
AddComponent<Hand>();
human.AddComponent<Leg>();
```
In ET, all data is Entity, including Entity, and Entity can be used either as a component or as a child of other Entity. Generic data is placed on Entity as a member, and less generic data can be hung on Entity as a component. For example, the design of items, all items have fields configured id, quantity, level, these fields are not necessary to make components, put on Entity will be more convenient to use.
```csharp
class Item: Entity
{
// The configuration Id of the props
public int ConfigId { get; set; }
// The number of props
public int Count { get; set; }
// The level of the props
public int Level { get; set; }
}
```
This design data of ET is a kind of tree structure, very hierarchical and can understand the whole game architecture very easily. Scene, and the data of different modules are mounted on top of Game. Scene, and each module can mount a lot of data under itself. You don't have to think too much about how to design the classes, where to put the data, whether mounting here will lead to redundancy, etc. for each new feature. For example, if my player needs to do a prop system, I can design an ItemsComponent to be mounted on the Player, and I need to develop a SpellComponent to be mounted on the Player for skills. If the whole service needs to do an activity, get an activity component to hang on top of the Game. This design will be very easy to assign tasks and very modular.
# Some details of the component
### 1. Component creation
ComponentFactory provides three methods to create components Create, CreateWithParent, CreateWithId. Create is the simplest way to create components, it does several things
a. Construct a component based on the component type
b. Add the component to the event system and throw an AwakeSystem
c. Enables object pooling or not
CreateWithParent provides a Parent object on top of Create, which is set to the Component.Parent field. createWithId is used to create ComponentWithId or its subclasses, and you can set an Id on top of Create itself, Component can choose whether to use a pool of objects when it is created. All three types of factory methods have a fromPool parameter, the default is true.
### 2. Release of components
Component inherits an IDisposable interface. It should be noted that Component has unmanaged resources and must be called to delete a Component. This interface does the following
a. Throw Destroy System
b. If the component was created using an object pool, then it is put back into the object pool here
c. Remove the component from the global EventSystem and set the InstanceId to 0
If the component is mounted on Entity, then when Entity calls Dispose, it will automatically call the Dispose method of all components on it.
### 3. Role of InstanceId
Any Component comes with an InstanceId field, which is reset when the component is constructed, or when the component is removed from the object pool, and which identifies the identity of the component. Why is such a field needed? There are several reasons
1. the existence of the object pool, the component may not be released, but returned to the object pool. In an asynchronous call, it is likely that the component has already been released and then reused, so that we need a way to be able to distinguish whether the previous component object has been released, such as the following code.
```csharp
public static async ETVoid UpdateAsync(this ActorLocationSender self)
{
try
{
long instanceId = self.InstanceId;
while (true)
{
if (self.InstanceId ! = instanceId)
{
return;
}
ActorTask actorTask = await self.GetAsync();
if (self.InstanceId ! = instanceId)
{
return;
}
if (actorTask.ActorRequest == null)
{
return;
}
await self.RunTask(actorTask);
}
}
catch (Exception e)
{
Log.Error(e);
}
}
```
While (true) is an asynchronous method, after await self.GetAsync() it is likely that the ActorLocationSender object has been released, and it is even possible that the object has been utilized again by other logic from the object pool. We can determine whether the object has been released by the change of InstanceId. 2.
2. InstanceId is globally unique and has location information, so you can find the location of the object by InstanceId and send the message to the object. This design will be utilized in the Actor message. Here for the time being will not talk about it.
================================================
FILE: Book/3.3一切皆实体.md
================================================
# 一切皆实体
目前十分流行ECS设计,主要是守望先锋的成功,引爆了这种技术。守望先锋采用了状态帧这种网络技术,客户端会进行预测,预测不准需要进行回滚,由于组件式的设计,回滚可以只回滚某些组件即可。ECS最重要的设计是逻辑跟数据的完全分离。即EC是纯数据,System实际上就是逻辑,由数据驱动逻辑。数据驱动逻辑是什么意思呢?很简单通过Update检测数据变化,通过事件机制来订阅数据变化,这就是所谓的数据驱动了。其它的特点例如缓存命中,在编写逻辑上来说并不太重要,现代游戏都用脚本,连脚本的性能都能容忍怎么会在乎缓存命中那点性能提升?ET在设计的时候吸收了这些想法,但是并不完全照搬,目前的设计是我经过长期的思考跟重构得来的,还是有些自己特色。
传统的ECS写逻辑作者看来存在不少缺陷,比如为了复用,数据必然要拆成非常小的颗粒,会导致组件非常非常多。但是游戏是多人合作开发的,每个人基本上只熟悉自己的模块,最后可能造成组件大量冗余。还有个问题,常见的ECS是扁平式的,Entity跟Component只有一层。组件一多,开发功能可能不知道该使用哪些Component。好比一家公司,最大的是老板,老板手下带几百个人,老板不可能认识所有的人,完成一项任务,老板没法挑出自己需要的人。合理的做法是老板手下应该有几个经理,每个经理手下应该有几个主管,每个主管管理几个工人,这样形成树状的管理结构才会容易管理。这类似ET的做法,Entity可以管理Component,Component管理Entity,甚至Component还可以挂载Component。例如:人由头,身体,手,脚组成,而头又由眼睛,耳朵,鼻子,嘴巴组成。
```csharp
Head head = human.AddComponent<Head>();
head.AddComponent<Eye>();
head.AddComponent<Mouse>();
head.AddComponent<Nose>();
head.AddComponent<Ear>();
human.AddComponent<Body>();
human.AddComponent<Hand>();
human.AddComponent<Leg>();
```
ET中,所有数据都是Entity,包括Entity,Entity既可以当成组件使用,也可以当做其它Entity的孩子。通用的数据放在Entity身上作为成员,不太通用的数据可以作为组件挂在Entity身上。比如物品的设计,所有物品都有配置id,数量,等级的字段,这些字段没有必要做成组件,放在Entity身上使用会更加方便。
```csharp
class Item: Entity
{
// 道具的配置Id
public int ConfigId { get; set; }
// 道具的数量
public int Count { get; set; }
// 道具的等级
public int Level { get; set; }
}
```
ET的这种设计数据是一种树状的结构,非常有层次,能够非常轻松的理解整个游戏的架构。顶层Game.Scene,不同模块的数据都挂载在Game.Scene上面,每个模块自身下面又可以挂载很多数据。每开发一个新功能不用思考太多,类该怎么设计,数据放在什么地方,挂载这里会不会导致冗余等等。比如我玩家需要做一个道具系统,设计一个ItemsComponent挂在Player身上即可,需要技能开发一个SpellComponent挂在Player身上。全服需要做一个活动,搞个活动组件挂在Game.Scene上面。这种设计任务分派会很简单,十分的模块化。
# 组件的一些细节
### 1.组件的创建
组件的创建不要自己去new,应该统一使用ComponentFactory创建。ComponentFactory提供了三组方法用来创建组件Create,CreateWithParent,CreateWithId。Create是最简单的创建方式,它做了几个处理
a. 根据组件类型构造一个组件
b. 将组件加入事件系统,并且抛出一个AwakeSystem
c. 是否启用对象池
CreateWithParent在Create的基础上提供了一个Parent对象,设置到Component.Parent字段上。CreateWithId是用来创建ComponentWithId或者其子类的,在Create的基础上可以自己设置一个Id, Component在创建的时候可以选择是否使用对象池。三类工厂方法都带有一个fromPool的参数,默认是true。
### 2.组件的释放
Component都继承了一个IDisposable接口,需要注意,Component有非托管资源,删除一个Component必须调用该接口。该接口做了如下的操作
a. 抛出Destroy System
b. 如果组件是使用对象池创建的,那么在这里会放回对象池
c. 从全局事件系统(EventSystem)中删除该组件,并且将InstanceId设为0
如果组件挂载Entity身上,那么Entity调用Dispose的时候会自动调用身上所有Component的Dispose方法。
### 3.InstanceId的作用
任何Component都带有一个InstanceId字段,这个字段会在组件构造,或者组件从对象池取出的时候重新设置,这个InstanceId标识这个组件的身份。为什么需要这么一个字段呢?有以下几个原因
1. 对象池的存在,组件未必会释放,而是回到对象池中。在异步调用中,很可能这个组件已经被释放了,然后又被重新利用了起来,这样我们需要一种方式能区分之前的组件对象是否已经被释放,例如下面这段代码:
```csharp
public static async ETVoid UpdateAsync(this ActorLocationSender self)
{
try
{
long instanceId = self.InstanceId;
while (true)
{
if (self.InstanceId != instanceId)
{
return;
}
ActorTask actorTask = await self.GetAsync();
if (self.InstanceId != instanceId)
{
return;
}
if (actorTask.ActorRequest == null)
{
return;
}
await self.RunTask(actorTask);
}
}
catch (Exception e)
{
Log.Error(e);
}
}
```
while (true)中是段异步方法,await self.GetAsync()之后很可能ActorLocationSender对象已经被释放了,甚至有可能这个对象又被其它逻辑从对象池中再次利用了起来。我们这时候可以通过InstanceId的变化来判断这个对象是否已经被释放掉。
2. InstanceId是全局唯一的,并且带有位置信息,可以通过InstanceId来找到对象的位置,将消息发给对象。这个设计将会Actor消息中利用到。这里暂时就不讲了。
================================================
FILE: Book/3.4EventSystem.md
================================================
# EventSystem
One of the most important features of ECS is the separation of data and logic, and the second is data-driven logic. What is data-driven logic? Not very well understood, let's take an example
A moba game, the heroes have blood bars, which are displayed on the character's head, and also on the top left avatar UI. This time the server sends a blood deduction message. How do we handle this message? In the first way, we modify the hero's blood value in the message handling function, modify the blood bar display on the avatar, and modify the blood bar on the avatar UI at the same time. This approach obviously causes coupling between modules. In the second method, the blood value is only changed in the blood deduction message processing function, and the change of blood value throws an hpchange event, and both the avatar module and the UI module subscribe to the blood value change event and handle their own logic in the subscribed method, so that each module is responsible for its own logic without coupling.
ET provides a variety of events, all of which can be subscribed multiple times:
1. AwakeSystem, which is thrown after the component factory creates a component, and is thrown only once, with parameters
```csharp
Player player = ComponentFactory.Create<Player>();
// Subscribe to Player's Awake event
public class PlayerAwakeSystem: AwakeSystem<Player>
{
public override void Awake(Player self)
{
}
}
````
2. StartSystem, component UpdateSystem call before throwing
```csharp
// Subscribe to Player's Start event
public class PlayerStartSystem: StartSystem<Player>
{
public override void Start(Player self)
{
}
}
````
3. UpdateSystem, component thrown every frame
```csharp
// Subscribe to Player's Update event
public class PlayerUpdateSystem: UpdateSystem<Player>
{
public override void Update(Player self)
{
}
}
````
4. DestroySystem, thrown when the component is deleted
```csharp
// Subscribe to Player's Destroy event
public class PlayerDestroySystem: DestroySystem<Player>
{
public override void Destroy(Player self)
{
}
}
Player player = ComponentFactory.Create<Player>();
// The Destroy event will be fired here
player.Dispose();
```
5. ChangeSystem, thrown when the content of the component changes, needs to be triggered manually by the developer
```csharp
// Subscribe to Player's Destroy event
public class PlayerChangeSystem: ChangeSystem<Player>
{
public override void Change(Player self)
{
}
}
Player player = ComponentFactory.Create<Player>();
// need to trigger ChangeSystem manually
Game.EventSystem.Change(player);
```
6. DeserializeSystem, thrown after component deserialization
```csharp
// Subscribe to Player's Deserialize event
public class PlayerDeserializeSystem: DeserializeSystem<Player>
{
public override void Deserialize(Player self)
{
}
}
// Here player2 will trigger the Deserialize event
Player player2 = MongoHelper.FromBson<Player>(player.ToBson());
```
7. LoadSystem, EventSystem thrown when loading dll, used for server-side hot update, reload dll to do some processing, such as re-register handler
```csharp
// Subscribe to Player's Load event
public class PlayerLoadSystem: LoadSystem<Player>
{
public override void Load(Player self)
{
}
}
```
8. normal Event, thrown by the developer himself, can take up to three parameters. Also the client hot change layer can subscribe to the mono layer Event events
```csharp
int oldhp = 10;
int newhp = 5;
// Throw hp change event
Game.EventSystem.Run("HpChange", oldhp, newhp);
// UI subscribe to the hp change event
[Event("HpChange")]
public class HpChange_ShowUI: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
// The model header blood bar module also subscribes to the hp change event
[Event("HpChange")
public class HpChange_ModelHeadChange: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
```
9. There are many other events, such as message events. Message events are declared using MessageHandler and can take parameters to specify which server to subscribe to.
```csharp
[MessageHandler(AppType.Gate)]
public class C2G_LoginGateHandler : AMRpcHandler<C2G_LoginGate, G2C_LoginGate>
{
protected override void Run(Session session, C2G_LoginGate message, Action<G2C_LoginGate> reply)
{
G2C_LoginGate response = new G2C_LoginGate();
reply(reply);
}
}
```
More specific message events will be explained in detail when we talk about messages
10. numeric events, numeric module and then explain
...... , more events to be developed by yourself.
The logic of the ET framework is driven by the various events above.
================================================
FILE: Book/3.4事件机制EventSystem.md
================================================
# 事件机制EventSystem
ECS最重要的特性一是数据跟逻辑分离,二是数据驱动逻辑。什么是数据驱动逻辑呢?不太好理解,我们举个例子
一个moba游戏,英雄都有血条,血条会在人物头上显示,也会在左上方头像UI上显示。这时候服务端发来一个扣血消息。我们怎么处理这个消息?第一种方法,在消息处理函数中修改英雄的血数值,修改头像上血条显示,同时修改头像UI的血条。这种方式很明显造成了模块间的耦合。第二种方法,扣血消息处理函数中只是改变血值,血值的改变抛出一个hpchange的事件,人物头像模块跟UI模块都订阅血值改变事件,在订阅的方法中分别处理自己的逻辑,这样各个模块负责自己的逻辑,没有耦合。
ET提供了多种事件,事件都是可以多次订阅的:
1. AwakeSystem,组件工厂创建组件后抛出,只抛出一次,可以带参数
```csharp
Player player = ComponentFactory.Create<Player>();
// 订阅Player的Awake事件
public class PlayerAwakeSystem: AwakeSystem<Player>
{
public override void Awake(Player self)
{
}
}
```
2. StartSystem,组件UpdateSystem调用前抛出
```csharp
// 订阅Player的Start事件
public class PlayerStartSystem: StartSystem<Player>
{
public override void Start(Player self)
{
}
}
```
3. UpdateSystem,组件每帧抛出
```csharp
// 订阅Player的Update事件
public class PlayerUpdateSystem: UpdateSystem<Player>
{
public override void Update(Player self)
{
}
}
```
4. DestroySystem,组件删除时抛出
```csharp
// 订阅Player的Destroy事件
public class PlayerDestroySystem: DestroySystem<Player>
{
public override void Destroy(Player self)
{
}
}
Player player = ComponentFactory.Create<Player>();
// 这里会触发Destroy事件
player.Dispose();
```
5. ChangeSystem,组件内容改变时抛出,需要开发者手动触发
```csharp
// 订阅Player的Destroy事件
public class PlayerChangeSystem: ChangeSystem<Player>
{
public override void Change(Player self)
{
}
}
Player player = ComponentFactory.Create<Player>();
// 需要手动触发ChangeSystem
Game.EventSystem.Change(player);
```
6. DeserializeSystem,组件反序列化之后抛出
```csharp
// 订阅Player的Deserialize事件
public class PlayerDeserializeSystem: DeserializeSystem<Player>
{
public override void Deserialize(Player self)
{
}
}
// 这里player2会触发Deserialize事件
Player player2 = MongoHelper.FromBson<Player>(player.ToBson());
```
7. LoadSystem,EventSystem加载dll时抛出,用于服务端热更新,重新加载dll做一些处理,比如重新注册handler
```csharp
// 订阅Player的Load事件
public class PlayerLoadSystem: LoadSystem<Player>
{
public override void Load(Player self)
{
}
}
```
8. 普通的Event,由开发者自己抛出,可以最多带三个参数。另外客户端热更层也可以订阅mono层的Event事件
```csharp
int oldhp = 10;
int newhp = 5;
// 抛出hp改变事件
Game.EventSystem.Run("HpChange", oldhp, newhp);
// UI订阅hp改变事件
[Event("HpChange")]
public class HpChange_ShowUI: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
// 模型头顶血条模块也订阅hp改变事件
[Event("HpChange")]
public class HpChange_ModelHeadChange: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
```
9. 除此之外还有很多事件,例如消息事件。消息事件使用MessageHandler来声明,可以带参数指定哪种服务器需要订阅。
```csharp
[MessageHandler(AppType.Gate)]
public class C2G_LoginGateHandler : AMRpcHandler<C2G_LoginGate, G2C_LoginGate>
{
protected override void Run(Session session, C2G_LoginGate message, Action<G2C_LoginGate> reply)
{
G2C_LoginGate response = new G2C_LoginGate();
reply(response);
}
}
```
更具体的消息事件等到讲消息的时候再细细讲解了
10. 数值事件,数值模块再讲解
......, 更多的事件由自己去开发。
ET框架的逻辑就是由以上各种事件来驱动的。
================================================
FILE: Book/4.1Component-based design.md
================================================
# Component-based design
In terms of code reuse and organization of data, object-oriented may be the first response. Object-oriented three features inheritance, encapsulation, polymorphism, to a certain extent can solve a lot of code reuse, data reuse problems. But object-oriented is not a panacea, it also has great flaws: # # 1.
## 1. data structure coupling is very strong
Once a field is added or removed from a parent class, it may have to affect all subclasses and affect all subclass-related logic. This seems very inflexible, in a complex set of inheritance system, to change the fields in the parent class will become more and more troublesome, let's say ABC is a subclass of D, one day found the need to add a data that AB has, but C does not, then this data is certainly not good to put into the parent class, only AB abstracted out of a parent class E, E inherited from D, AB common fields added to E, once the inheritance structure Once the inheritance structure has changed, the interface may also need to change, let's say there is an interface incoming parameter type is E, when AB no longer needs the common field, then the inheritance relationship needs to be adjusted so that AB inherits D again, then the interface incoming parameter type needs to be changed to D, where the logic code is likely to be adjusted. What's worse is that the game logic changes very complicated and very often, maybe today a field is added, tomorrow it is deleted, if every time to adjust the inheritance structure, it is a nightmare. Inheritance structure feels very powerless in the face of frequent data structure adjustment.
## 2. Difficult to hot-plug
Inheritance structure can't add or delete fields at runtime, for example, Player usually walks, and then rides after using mount. The problem is that the information about the mount would need to be hanging on top of the Player object all the time. This makes it inflexible. Why do I need to have the horse data in memory when I'm not riding? The interface has the same problem, a class implements an interface, then the interface will always stick to the class, you want to get rid of her can not, or horse riding for example, the player player can ride, then may inherit a riding interface, the problem is, when I the Player from the mount, the player player still has the riding interface, there is no way to There is no way to dynamically delete this interface! The example may not be quite right, but the reasoning should be clear.
The use of object-oriented may lead to disastrous consequences, game development in the new and old, there are good skills, there are poor skills. People like to be lazy, when you find the trouble to adjust the inheritance relationship, it is possible to add a field in AB to save trouble directly into the parent class D. The result is that C somehow has an extra field that is useless. The key can not be found, and finally lead to the parent class D is getting bigger and bigger, in the end, it may simply not ABC, directly let all the objects into D, convenient well! Yes, many games are doing this, the development of the end simply do not care about the inheritance relationship, because you want to manage can not manage.
## 3. method and data coupling
Traditional object-oriented are class with methods, and especially advocate virtual function polymorphism. Methods and data put together brings a lot of coupling problems. In order to solve these coupling, we came up with a large number of design patterns, such as dependency interfaces, dependency transposition. To be honest, this is pants down, in order to understand the coupling, make the class into an interface, and then inherit from the interface, is this not called dependency? These practices lead to code that is full of interfaces and extremely difficult to read. There is no standard for writing up code, and the code written by experts and rookies is completely different. Most coders are logic boys, who have time to think about how to design this class every day ah? As the logic becomes more and more complex class inside the method will become increasingly large, the terrible thing is that this is the method of the class, extremely difficult to refactor, many projects can see the class inside the existence of tens of thousands of lines of code of virtual functions. Gosh!
Object-oriented in the face of complex game logic is very powerless, so many game developers have gone backwards, using process-oriented development game, process-oriented, simple and brutal, do not consider complex inheritance, do not consider abstraction, do not consider polymorphism, is the development of the session of freestyle, roll up your sleeves on the jerk, but at the same time, the reusability of the code logic, data reusability is also greatly reduced. Process-oriented is also not a good game development model.
Component pattern is a good solution to object-oriented and process-oriented defects, in the game client is very widely used, Unity3d, Unreal 4, and so on are using the component pattern. The characteristics of the component pattern.
1. Highly modular, a component is a piece of data plus a paragraph of logic
2. Components can be hot-pluggable, you need to add, do not need to remove
3. Very few dependencies between types, any type of adding or removing components will not affect other types.
But at present only a very small number of server-side design using components, Watchtower server-side should be the use of component design, Watchtower developers called ECS architecture, in fact, is a variant of the component model, E is Entity, C is Component, S is System, in fact, is the component Component logic and data stripped, the logic part called System, the topic is far away, or back to the ET framework to put.
ET framework uses the design of components. Everything is Entity and Component, any class inherited from Entity can be mounted components, such as the player class.
```C#
public sealed class Player : Entity
{
public string Account { get; private set; }
public long UnitId { get; set; }
public void Awake(string account)
{
this.Account = account;
}
public override void Dispose()
{
if (this.Id == 0)
{
return;
}
base.Dispose();
}
}
```
Mount a MoveComponent to the player object so that the player can move, a Backpack component to the player so that the player can manage items, a Skill component to the player so that the player can cast skills, and a Buff component to manage buffs.
```C#
player.AddComponent<MoveComponent>();
player.AddComponent<ItemsComponent>();
player.AddComponent<SpellComponent>();
AddComponent<BuffComponent>(); player.AddComponent<BuffComponent>();
```
Components are highly reusable, for example, an NPC, he can also move, just put MoveComponent on the NPC, some NPCs can also cast skills, then put SpellComponent on it, NPCs do not need a backpack, then there is no need to hang ItemsComponent
ET framework modules are all made into the form of components, a process is also made of different components stitched together. Let's say Loginserver needs to connect externally and also needs to connect with the server internally, then login server hooks up
```C#
// intranet network component NetInnerComponent, which handles the connection to the intranet
Game.Scene.AddComponent<NetInnerComponent, string, int>(innerConfig.Host, innerConfig.Port);
// NetOuterComponent, an extranet component, handles the connection to the client
Game.Scene.AddComponent<NetOuterComponent, string, int>(outerConfig.Host, outerConfig.Port);
```
For example, battle server does not need to connect to the external network (external network messages are forwarded by gateserver), so it is natural to just mount an internal network component.
Similar to Unity3d components, ET framework also provides component events, such as Awake, Start, Update, etc.. To add these events to a Component or Entity, you must write a helper class. For example, if the NetInnerComponent component needs Awake and Update methods, then add a class like this.
```C#
[ObjectEvent]
public class NetInnerComponentEvent : ObjectEvent<NetInnerComponent>, IAwake, IUpdate
{
public void Awake()
{
this.Get().Awake();
}
public void Update()
{
this.Get().Update();
}
}
````
In this way, NetInnerComponent calls its Awake method after AddComponent and calls the Update method every frame.
ET does not use reflection like Unity to implement this kind of functionality, because reflection performance is poor, and the advantage of this implementation is that the class can be placed in the hot update dll, so that the component's Awake Start, Update method and other methods can be placed in the hot update layer. Entity and Component will be made into a class without methods, methods are put into the hot update layer to facilitate the hot update to fix logic bugs.
The biggest advantage of component-based development is that whether rookie or expert, the development of a function can quickly know how to organize data how to organize the logic. Object-oriented can be completely abandoned. Object-oriented development using the most headache is that I should inherit which class it? Before doing the most horrible is Unreal three, Unreal three inheritance structure is very multi-layer, completely do not know where they need to start inheritance. In the end, it could lead to a very small function that inherits a very large class, which is common in Unreal 3 development. So Unreal 4 switched to the component pattern. The module isolation of the component pattern is very good, the technical rookie a component written very poorly, will not affect the other modules, the big deal is to rewrite the component on the good.
ET's component design innovation, method and data separation, completely decoupled, no brainstorming how to uncouple, write static methods at will, there is no coupling, even the code written by rookies is easy to refactor.
It is because ET uses the detachable component model, ET can load all server components to the same process, then this one process can be used as a set of distributed servers. From then on it is possible to debug distributed servers with vs. Because of this, the usual development only use a process, when the release of the release into multiple processes on the line. Honestly, not to brag, this is a great invention, this invention solves a big problem in the development of distributed game servers, greatly improving the efficiency of development.
================================================
FILE: Book/4.1组件式设计.md
================================================
# 组件式设计
在代码复用和组织数据方面,面向对象可能是大家第一反应。面向对象三大特性继承,封装,多态,在一定程度上能解决不少代码复用,数据复用的问题。不过面向对象不是万能的,它也有极大的缺陷:
## 1. 数据结构耦合性极强
一旦父类中增加或删除某个字段,可能要影响到所有子类,影响到所有子类相关的逻辑。这显得非常不灵活,在一套复杂的继承体系中,往父类中改变字段会变得越来越麻烦,比方说ABC是D的子类,某天发现需要增加一个AB都有的数据,但是C没有,那么这个数据肯定不好放到父类中,只能将AB抽象出来一个父类E,E继承于D,AB共有的字段加到E中,一旦继承结构发生了变化,可能接口也要改变,比方说之前有个接口传入参数类型是E,当AB不再需要共用的那个字段,那么需要调整继承关系,让AB重新继承D,那么这个接口的传入参数类型需要改成D,其中的逻辑代码很可能也要发生调整。更可怕的是游戏逻辑变化非常复杂,非常频繁,可能今天加了个字段,明天又删掉了,假如每次都要去调整继承结构,这简直就是噩梦。继承结构面对频繁的数据结构调整感觉很无力。
## 2. 难以热插拔
继承结构无法运行时增加删除字段,比如玩家Player平常是走路,使用坐骑后就骑马。问题是坐骑的相关信息就需要一直挂在Player对象上面。这就显得很不灵活,我不骑马的时候内存中为啥要有马的数据?接口也有同样的问题,一个类实现了一个接口,那么这个接口就永远粘在了这个类身上,你想甩掉她都不行,还是以骑马为例,玩家Player可以进行骑行,那么可能继承一个骑行的接口,问题是,当我这个Player从坐骑上下来时,玩家Player身上还是有骑行的接口,根本没法动态删掉这个接口!可能例子举得不是很对,但是道理表述的应该很清楚了。
使用面向对象可能导致灾难性后果,游戏开发中有新人有老人,有技术好的,有技术差的。人都是喜欢偷懒的,当你发现调整继承关系麻烦的时候,有可能AB中增加一个字段为了省事直接就放到父类D中去了。导致C莫名奇妙的多了一个无用的字段。关键还没法发现,最后导致父类D越来越大,到最后有可能干脆就不用ABC了,直接让所有对象都变成D,方便嘛!是的,很多游戏就是这么干的,开发到最后根本就不管继承关系了,因为想管也管不了了。
## 3. 方法与数据耦合
传统面向对象都是class中带有方法,并且特别提倡虚函数多态。方法跟数据放在一起带来了特别多耦合的问题。为了解决这些耦合,大家想出了大量的设计模式,比如依赖接口,依赖转置。说实话,这就是脱裤子放屁,为了解耦合,把类做成接口,然后继承接口,难道这就不叫依赖了?这些做法导致,代码中到处是接口,代码阅读极其困难。写起代码来也没有个标准,高手跟菜鸡写出来的代码完全是两回事。大部分码农都是逻辑仔,谁有时间天天想这个类要怎么设计啊?随着逻辑越来越复杂类里面的方法将越来越庞大,可怕的是,这是这个类的方法,极其难以重构,很多项目中能看到类里面存在上万行代码的虚函数。天哪!
面向对象在面对复杂的游戏逻辑时很无力,所以很多游戏开发者又倒退了回去,使用面向过程进行开发游戏,面向过程,简单粗暴,不考虑复杂的继承,不考虑抽象,不考虑多态,是开发届的freestyle,挽起袖子就开撸,但同时,代码逻辑的复用性,数据的复用性也大大降低。面向过程也不是一种好的游戏开发模式。
组件模式很好的解决了面向对象以及面向过程的种种缺陷,在游戏客户端中使用非常广泛,Unity3d,虚幻4,等等都使用了组件模式。组件模式的特点:
1.高度模块化,一个组件就是一份数据加一段逻辑
2.组件可热插拔,需要就加上,不需要就删除
3.类型之间依赖极少,任何类型增加或删除组件不会影响到其它类型。
但是目前只有极少有服务端使用了组件的设计,守望先锋服务端应该是使用了组件的设计,守望先锋的开发人员称之为ECS架构,其实就是组件模式的一个变种,E就是Entity,C就是Component,S是System,其实就是将组件Component的逻辑与数据剥离,逻辑部分叫System,话题扯远了,还是回到ET框架来把。
ET框架使用了组件的设计。一切都是Entity和Component,任何类继承于Entity都可以挂载组件,例如玩家类:
```C#
public sealed class Player : Entity
{
public string Account { get; private set; }
public long UnitId { get; set; }
public void Awake(string account)
{
this.Account = account;
}
public override void Dispose()
{
if (this.Id == 0)
{
return;
}
base.Dispose();
}
}
```
给玩家对象挂载个移动组件MoveComponent,这样玩家就可以移动了,给玩家挂上一个背包组件,玩家就可以管理物品了,给玩家挂上技能组件,那么玩家就可以施放技能了,加上Buff组件就可以管理buff了。
```C#
player.AddComponent<MoveComponent>();
player.AddComponent<ItemsComponent>();
player.AddComponent<SpellComponent>();
player.AddComponent<BuffComponent>();
```
组件是高度可以复用的,比如一个NPC,他也可以移动,给NPC也挂上MoveComponent就行了,有的NPC也可以施放技能,那么给它挂上SpellComponent,NPC不需要背包,那么就不用挂ItemsComponent了
ET框架模块全部做成了组件的形式,一个进程也是由不同的组件拼接而成。比方说Loginserver需要对外连接也需要与服务器内部进行连接,那么login server挂上
```C#
// 内网网络组件NetInnerComponent,处理对内网连接
Game.Scene.AddComponent<NetInnerComponent, string, int>(innerConfig.Host, innerConfig.Port);
// 外网网络组件NetOuterComponent,处理与客户端连接
Game.Scene.AddComponent<NetOuterComponent, string, int>(outerConfig.Host, outerConfig.Port);
```
比如battle server就不需要对外网连接(外网消息由gateserver转发),那么很自然的只需要挂载一个内网组件即可。
类似Unity3d的组件,ET框架也提供了组件事件,例如Awake,Start,Update等。要给一个Component或者Entity加上这些事件,必须写一个辅助类。比如NetInnerComponent组件需要Awake跟Update方法,那么添加一个这样的类即可:
```C#
[ObjectEvent]
public class NetInnerComponentEvent : ObjectEvent<NetInnerComponent>, IAwake, IUpdate
{
public void Awake()
{
this.Get().Awake();
}
public void Update()
{
this.Get().Update();
}
}
```
这样,NetInnerComponent在AddComponent之后会调用其Awake方法,并且每帧调用Update方法。
ET没有像Unity使用反射去实现这种功能,因为反射性能比较差,而且这样实现的好处是这个类可以放到热更dll中,这样组件的Awake Start,Update方法以及其它方法都可以放到热更层中。将Entity和Component做成没有方法的类,方法都放到热更层,方便热更修复逻辑bug。
组件式开发最大的好处就是不管菜鸟还是高手,开发一个功能都能很快的知道怎么组织数据怎么组织逻辑。可以完全放弃面向对象。使用面向对象开发最头疼的就是我该继承哪个类呢?之前做过最恐怖的就是虚幻三,虚幻三的继承结构非常多层,完全不知道自己需要从哪里开始继承。最后可能导致一个非常小的功能,继承了一个及其巨大的类,这在虚幻三开发中屡见不鲜。所以虚幻4改用了组件模式。组件模式的模块隔离性非常好,技术菜鸟某个组件写得非常差,也不会影响到其它模块,大不了重写这个组件就好了。
ET的组件设计有所创新,方法跟数据分离,完全解除耦合,不用绞尽脑汁去想怎么解除耦合,随意写静态方法即可,根本不存在耦合,即使是菜鸟写的代码也很容易重构。
正是因为ET使用了可拆卸的组件模式,ET可以将所有服务器组件都装到同一个进程上,那么这一个进程就可以当作一组分布式服务器使用。从此用vs调试分布式服务器成为了可能。正因为这样,平常开发只使用一个进程,发布的时候发布成多个进程就行了。说实在的,不是吹牛,这是一个伟大的发明,这一发明解决了分布式游戏服务器开发中的大大大难题,极大的提高了开发效率。
================================================
FILE: Book/5.4Actor Model.md
================================================
# Actor model
### Actor introduction
Before discussing the Actor model, we should discuss the architecture of ET. There are two architectures for game servers in order to use multi-core, single-threaded multi-process and single-process multi-threaded architecture. ET uses single-threaded multi-process architecture, while the traditional Actor model is generally single-process multi-threaded architecture, which is a major difference. The advantages and disadvantages are as follows.
1. the logic needs to be single-threaded this is the same, erlang process logic is single-threaded, skynet lua virtual machine is also single-threaded. et in a process is actually equivalent to an erlang process, a skynet lua virtual machine.
2. the use of single-threaded multi-process does not need to write their own set of profiler tools, you can use a lot of ready-made profiler tools, such as view memory, cpu occupation directly with top command, this point erlang and skynet need to get another set of tools.
3. multi-process single-threaded architecture also has a benefit, a single physical machine and multiple physical machines is no difference, single process multi-threaded also need to consider the processing of multiple physical machines.
4. multi-process single-threaded architecture is a bit of a drawback is that messages need to be serialized and deserialized across processes, taking up a bit of resources. In addition, sending network messages will have a few milliseconds delay. Generally these effects can be ignored.
The original Actor model is used for single-process multi-threaded architecture, there is a reason for this, because multi-threaded architecture developers can easily access shared variables at will, let's say a variable a, thread 1 can access, thread 2 can also access, so that both threads need to add locks when accessing the variable a, shared variables more locks everywhere, will become unmaintainable, the framework must not appear The framework must not have a situation where there are threads sharing variables everywhere. In order to ensure that the multi-threaded architecture does not go wrong, it is necessary to provide a development model that ensures easy and safe multi-threaded development. erlang's concurrency mechanism is the actor model. erlang virtual machine uses multiple threads to take advantage of multiple cores. erlang has designed a mechanism that designs its own processes on top of the virtual machine. At its simplest, each erlang process manages its own variables, and the logic of each erlang process runs on a single thread. The logic between the erlang process and the process is completely isolated, so that there are no two threads accessing the same variable and there is no multithreaded competition. The next question arises, since each erlang process has its own data and the logic is completely isolated, how should the two erlang processes communicate with each other? This is where the Actor model comes in. erlang has designed a messaging mechanism: one process can send messages to other processes, and erlang processes communicate with each other through messages. Isn't this the same message queue used by operating systems for inter-process communication? Yes, in fact, it is similar. erlang inside the process id to get the process can send messages to this process.
If the message is only sent to the process, it is still a bit inconvenient. For example, if you take an erlang process as a moba team process, and there are 10 players in the battle process, if you use erlang's actor message, the message can only be sent to the battle process, but often the message needs to be sent to a player, then erlang needs to distribute the message to the specific player again according to the player id in the message, so it actually goes around one more time. This is actually an extra detour.
### ET's Actor
According to the characteristics of its own architecture, ET does not completely copy the Actor model of erlang, but provides the Entity object-level Actor model. In ET, an Actor is an Entity object, and a MailboxComponent component attached to an Entity is an Actor. You only need to know the Entity's InstanceId to send messages to the Entity. In fact, erlang's Actor model is a special case of ET, such as giving the ET server Game.Scene as an Actor, so that it can become a process-level Actor. It only needs to know the InstanceId (ET) or the Pid (erlang) of the process to send it to the other party.
| Language | ET | Erlang | Skynet |
| | ET | Erlang | Skynet | -- | :--: | :--: | :--: |
| Architecture | Single-Threaded Multi-Process | Single-Process Multi-Threaded | Single-Process Multi-Threaded |
| Actor | Entity | erlang process | lua virtual machine |
| ActorId | Entity.InstanceId | erlang processId | service address |
### Use of ET's Actor
For a normal Actor, we can refer to the Gate Session. map has a Unit, and the Unit holds the gate session corresponding to the player. thus, if a message in map needs to be sent to the client, it only needs to send the message to the gate session, and the gate session forwards it to the client when it receives the message. The map process sending messages to the gate session is a typical actor model. It doesn't need to know the location of the gate session, it just needs to know its InstanceId. messageHelper.cs gets an ActorMessageSender from GateSessionActorId and sends it.
```csharp
// Get an ActorSenderComponent from Game.Scene, then get an ActorMessageSender by InstanceId
ActorSenderComponent actorSenderComponent = Game.Scene.GetComponent<ActorSenderComponent>();
ActorMessageSender actorMessageSender = actorSenderComponent.Get(unitGateComponent.GateSessionActorId);
// send
actorMessageSender.Send(message);
// rpc
var response = actorMessageSender.Call(message);
```
The question is how do you know the InstanceId of the gate session in map? This is where you need to find a way to pass it on, for example in ET, when the player is logging in to gate, the gate session hooks up a mailbox MailBoxComponent, C2G_LoginGateHandler.cs
```csharp
session.AddComponent<MailBoxComponent, string>(MailboxType.GateSession);
```
The InstanceId of this gate session is brought into the map when the player logs into the map process, in C2G_EnterMapHandler.cs
```csharp
M2G_CreateUnit createUnit = (M2G_CreateUnit)await mapSession.Call(new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session. InstanceId });
```
### Handling of Actor messages
First, the message arrives at the MailboxComponent, which has a type, and different types of mailboxes can do different processing. Currently, there are two types of mailboxes, GateSession and MessageDispatcher; GateSession mailboxes will immediately forward messages to the client when they are received, and MessageDispatcher types will again distribute the Actor messages to the specific Handler for processing. The default MailboxComponent type is MessageDispatcher. customizing a mailbox type is also very simple, inherit the IMailboxHandler interface and add the MailboxHandler tag. So why do we need to add such a feature? This feature does not exist in other actor models, and messages are generally received and distributed. The reason is that GateSession is not designed for distribution, so I added the mailbox type here. messageDispatcher has two ways of handling messages, one is to handle the messages sent by the other party, and the other is rpc messages
```csharp
// To handle Send messages, you need to inherit the AMActorHandler abstract class. The first generic parameter of the abstract class is the type of the Actor, and the second parameter is the type of the message
[ActorMessageHandler(AppType.Map)]
public class Actor_TestHandler : AMActorHandler<Unit, Actor_Test>
{
protected override ETTask Run(Unit unit, Actor_Test message)
{
Log.Debug(message.Info);
}
}
// To handle Rpc messages, you need to inherit the AMActorRpcHandler abstract class, the first generic parameter of the abstract class is the type of the Actor, the second parameter is the type of the message, and the third parameter is the type of the returned message
[ActorMessageHandler(AppType.Map)]
public class Actor_TransferHandler : AMActorRpcHandler<Unit, Actor_TransferRequest, Actor_TransferResponse>
{
protected override async ETTask Run(Unit unit, Actor_TransferRequest message, Action<Actor_TransferResponse> reply)
{
Actor_TransferResponse response = new Actor_TransferResponse();
try
{
reply(response);
}
catch (Exception e)
{
ReplyError(response, e, reply);
}
}
}
```
We should note that Actor messages have the potential to deadlock, such as A call to B, B call to C, and C call to A. Because MailboxComponent is essentially a message queue, it opens a concurrent process that will process one message at a time, returning ETTask to indicate that the message processing class will block MailboxComponent queue of other messages. So if there is a deadlock, we don't want a message processing to block the rest of the MailboxComponent messages, we can just open a new thread in the message processing class to handle it. For example:
```csharp
[ActorMessageHandler(AppType.Map)]
public class Actor_TestHandler : AMActorHandler<Unit, Actor_Test>
{
protected override ETTask Run(Unit unit, Actor_Test message)
{
RunAsync(unit, message).Coroutine();
}
public ETVoid RunAsync(Unit unit, Actor_Test message)
{
Log.Debug(message.Info);
}
}
```
For related information, you can Google the Actor deadlock problem.
================================================
FILE: Book/5.4Actor模型.md
================================================
# Actor模型
### Actor介绍
在讨论Actor模型之前先要讨论下ET的架构,游戏服务器为了利用多核一般有两种架构,单线程多进程跟单进程多线程架构。两种架构本质上其实区别不大,因为游戏逻辑开发都需要用单线程,即使是单进程多线程架构,也要用一定的方法保证单线程开发逻辑。ET采用的是单线程多进程的架构,而传统Actor模型一般是单进程多线程的架构,这点是比较大的区别,不能说谁更好,只能说各有优势。优劣如下:
1. 逻辑需要单线程这点都是一样的,erlang进程逻辑是单线程的,skynet lua虚拟机也是单线程的。ET中一个进程其实相当于一个erlang进程,一个skynet lua虚拟机。
2. 采用单线程多进程不需要自己再写一套profiler工具,可以利用很多现成的profiler工具,例如查看内存,cpu占用直接用top命令,这点erlang跟skynet都需要自己另外搞一套工具。
3. 多进程单线程架构还有个好处,单台物理机跟多台物理机是没有区别的,单进程多线程还需要考虑多台物理机的处理。
4. 多进程单线程架构一点缺陷是消息跨进程需要进行序列化反序列化,占用一点资源。另外发送网络消息会有几毫秒延时。一般这些影响可以忽略。
最开始Actor模型是给单进程多线程架构使用的,这是有原因的,因为多线程架构开发者很容易随意的访问共享变量,比方说一个变量a, 线程1能访问,线程2也能访问,这样两个线程在访问变量a的时候都需要加锁,共享变量多了之后锁到处都是,会变得无法维护,框架肯定不能出现到处是线程共享变量的情况。为了保证多线程架构不出问题,必须提供一种开发模型保证多线程开发简单又安全。erlang语言的并发机制就是actor模型。erlang虚拟机使用多线程来利用多核。erlang设计了一种机制,它在虚拟机之上设计了自己的进程。最简单的,每个erlang进程都管理自己的变量,每个erlang进程的逻辑都跑在一个线程上,erlang进程跟进程之间逻辑完全隔离,这样就不存在两个线程访问同一变量的情况了也就不存在多线程竞争的问题。接下来问题又出现了,既然每个erlang进程都有自己的数据,逻辑完全是隔离的,两个erlang进程之间应该怎么进行通信呢?这时Actor模型就登场了。erlang设计了一种消息机制:一个进程可以向其它进程发送消息,erlang进程之间通过消息来进行通信,看到这会不会感觉很熟悉?这不就是操作系统进程间通信用的消息队列吗?没错,其实是类似的。erlang里面拿到进程的id就能给这个进程发送消息。
如果消息只发给进程其实还是有点不方便。比如拿一个erlang进程做moba战队进程,战斗进程中有10个玩家,如果使用erlang的actor消息,消息只能发送给战斗进程,但是很多时候消息是需要发送给一个玩家的,这时erlang需要根据消息中的玩家Id,把消息再次分发给具体的玩家,这样其实多绕了一圈。
### ET的Actor
ET根据自己架构得特点,没有完全照搬erlang的Actor模型,而是提供了Entity对象级别的Actor模型。这点跟erlang甚至传统的Actor机制不一样。ET中,Actor是Entity对象,Entity挂上一个MailboxComponent组件就是一个Actor了。只需要知道Entity的InstanceId就可以发消息给这个Entity了。其实erlang的Actor模型不过是ET中的一种特例,比如给ET服务端Game.Scene当做一个Actor,这样就可以变成进程级别的Actor。Actor本质就是一种消息机制,这种消息机制不用关心位置,只需要知道对方的InstanceId(ET)或者进程的Pid(erlang)就能发给对方。
| 语言 | ET | Erlang | Skynet |
| -- | :--: | :--: | :--: |
| 架构 | 单线程多进程 | 单进程多线程 | 单进程多线程 |
| Actor | Entity | erlang进程 | lua虚拟机 |
| ActorId | Entity.InstanceId | erlang进程Id | 服务地址 |
### ET的Actor的使用
普通的Actor,我们可以参照Gate Session。map中一个Unit,Unit身上保存了这个玩家对应的gate session。这样,map中的消息如果需要发给客户端,只需要把消息发送给gate session,gate session在收到消息的时候转发给客户端即可。map进程发送消息给gate session就是典型的actor模型。它不需要知道gate session的位置,只需要知道它的InstanceId即可。MessageHelper.cs中,通过GateSessionActorId获取一个ActorMessageSender,然后发送。
```csharp
// 从Game.Scene上获取ActorSenderComponent,然后通过InstanceId获取ActorMessageSender
ActorSenderComponent actorSenderComponent = Game.Scene.GetComponent<ActorSenderComponent>();
ActorMessageSender actorMessageSender = actorSenderComponent.Get(unitGateComponent.GateSessionActorId);
// send
actorMessageSender.Send(message);
// rpc
var response = actorMessageSender.Call(message);
```
问题是map中怎么才能知道gate session的InstanceId呢?这就是你需要想方设法传过去了,比如ET中,玩家在登录gate的时候,gate session挂上一个信箱MailBoxComponent,C2G_LoginGateHandler.cs中
```csharp
session.AddComponent<MailBoxComponent, string>(MailboxType.GateSession);
```
玩家登录map进程的时候会把这个gate session的InstanceId带进map中去,C2G_EnterMapHandler.cs中
```csharp
M2G_CreateUnit createUnit = (M2G_CreateUnit)await mapSession.Call(new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session.InstanceId });
```
### Actor消息的处理
首先,消息到达MailboxComponent,MailboxComponent是有类型的,不同的类型邮箱可以做不同的处理。目前有两种邮箱类型GateSession跟MessageDispatcher。GateSession邮箱在收到消息的时候会立即转发给客户端,MessageDispatcher类型会再次对Actor消息进行分发到具体的Handler处理,默认的MailboxComponent类型是MessageDispatcher。自定义一个邮箱类型也很简单,继承IMailboxHandler接口,加上MailboxHandler标签即可。那么为什么需要加这么个功能呢,在其它的actor模型中是不存在这个特点的,一般是收到消息就进行分发处理了。原因是GateSession的设计,并不需要进行分发处理,因此我在这里加上了邮箱类型这种设计。MessageDispatcher的处理方式有两种一种是处理对方Send过来的消息,一种是rpc消息
```csharp
// 处理Send的消息, 需要继承AMActorHandler抽象类,抽象类第一个泛型参数是Actor的类型,第二个参数是消息的类型
[ActorMessageHandler(AppType.Map)]
public class Actor_TestHandler : AMActorHandler<Unit, Actor_Test>
{
protected override ETTask Run(Unit unit, Actor_Test message)
{
Log.Debug(message.Info);
}
}
// 处理Rpc消息, 需要继承AMActorRpcHandler抽象类,抽象类第一个泛型参数是Actor的类型,第二个参数是消息的类型,第三个参数是返回消息的类型
[ActorMessageHandler(AppType.Map)]
public class Actor_TransferHandler : AMActorRpcHandler<Unit, Actor_TransferRequest, Actor_TransferResponse>
{
protected override async ETTask Run(Unit unit, Actor_TransferRequest message, Action<Actor_TransferResponse> reply)
{
Actor_TransferResponse response = new Actor_TransferResponse();
try
{
reply(response);
}
catch (Exception e)
{
ReplyError(response, e, reply);
}
}
}
```
我们需要注意一下,Actor消息有死锁的可能,比如A call消息给B,B call给C,C call给A。因为MailboxComponent本质上是一个消息队列,它开启了一个协程会一个一个消息处理,返回ETTask表示这个消息处理类会阻塞MailboxComponent队列的其它消息。所以如果出现死锁,我们就不希望某个消息处理阻塞掉MailboxComponent其它消息的处理,我们可以在消息处理类里面新开一个协程来处理就行了。例如:
```csharp
[ActorMessageHandler(AppType.Map)]
public class Actor_TestHandler : AMActorHandler<Unit, Actor_Test>
{
protected override ETTask Run(Unit unit, Actor_Test message)
{
RunAsync(unit, message).Coroutine();
}
public ETVoid RunAsync(Unit unit, Actor_Test message)
{
Log.Debug(message.Info);
}
}
```
相关资料可以谷歌一下Actor死锁的问题。
================================================
FILE: Book/5.5Actor Location-EN.md
================================================
# Actor Location
### Actor Location
Actor model only needs to know each other's InstanceId to send messages, which is very convenient, but sometimes we may not know each other's InstanceId, or an Actor's InstanceId will change. ET provides a mechanism to send messages to such objects, called Actor Location The mechanism is called Actor Location. The principle is relatively simple.
1. because the InstanceId is changing, the object's Entity.Id is unchanged, so we can first think of using Entity.Id to send actor messages
2. provide a location process (Location Server), the Actor object can store its Entity.Id and InstanceId as kv to the location process. Before sending the Actor message, go to the location process to look up the InstanceId of the Actor object before sending the actor message. 3.
3. When an Actor object is created in a process or migrated to a new process, it needs to register its Id and InstanceId to the Location Server. 4.
4. because the Actor object can be migrated, the message may be sent to the Actor has been migrated to other processes, so sending the Actor Location message needs to provide a reliable mechanism
5. ActorLocationSender provides two methods, Send and Call, Send a message also requires the recipient to return a message, and only when the return message is received will the next message be sent. 6.
6. If the Actor object is migrated away, it will return the error that the Actor does not exist, the sender will wait for 1 second after receiving this error, then go back to get the InstanceId of the Actor and resend it, currently it will try 5 times, after 5 times, it will throw an exception and report the error
7. ActorLocationSender will not query the Location Server every time it sends a message, because object migration is relatively rare after all, only the first time to query, then cache the InstanceId, and re-query after the failure to send.
8. actor object in the migration process, it is possible that other processes send over messages, when an error will occur, so the location server provides a Lock mechanism. Before the object is transmitted, the information in the process is deleted, and then a lock is added to the location server, and once the lock is on, other requests for the key will be queued.
9. before transmission because the other party deleted the actor of the process, so other processes will fail to send, then they will retry. When retrying, they will re-request the location server, which will be found to be locked, so they will keep waiting.
10. When the transmission is completed, the lock on the location server is unlocked, and the new address is updated, and then other location requests are responded to. Other requests sent to this actor continue.
Note that the Actor model is purely a server-side message communication mechanism, which has nothing to do with the client. We can use the server-side actor model for forwarding, so some client-side messages also inherit the actor interface. What happens if we don't use the actor interface on the client side? For example, the message Frame_ClickMap
```protobuf
message Frame_ClickMap // IActorLocationMessage
{
int64 ActorId = 93;
int64 Id = 94;
float X = 1;
float Y = 2;
float Z = 3;
}
```
We may not need the field ActorId, the message is sent to Gate, gate sees that it is a Frame_ClickMap message, it needs to be forwarded to the Unit on the Map, forwarding is still good, gate can get the location of the unit corresponding to the map from the session, and then forward it, the problem comes, Frame_ ClickMap message to the map, how does the map know which object the message needs to be given to? There are several designs at this point.
1. bring the Id of the unit in the underlying protocol of forwarding, which requires more complex underlying protocol support.
2. use a message to Frame_ClickMap message wrapping, wrapping the message with the Id of the Unit, wrapping with the message means greater consumption, increasing GC.
Personally, I feel that these two are very poor, not good, and even if distributed to the unit object processing, how to solve the problem of message re-entry it? unit object still needs to hang a message processing queue, and then receive the message thrown into the queue. Isn't this a duplication of the actor model? The current ET message sent to unit in the client did a design, the message into an actor message, gate received found to be an actor message, sent directly to the corresponding actor, the solution can be said to be beautiful. In fact, the client is still using session.send and call to send messages, send the message does not know that the message is an actor message, only to the gate, the gate judgment, refer to OuterMessageDispatcher.cs
### Actor Location message processing
ActorLocation messages are sent
```csharp
// Get the ActorLocationSenderComponent from Game.Scene, then get the ActorLocationSender via Entity.Id
ActorLocationSender actorLocationSender = Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
// Send the message through the ActorLocationSender
actorLocationSender.Send(actorLocationMessage);
// send the Rpc message
IResponse response = await actorLocationSender.Call(actorLocationRequest);
```
ActorLocation message processing is almost the same as Actor messages, the difference is that the two abstract classes inherited are different, note that the abstract class of actorlocation has an additional Location
```csharp
// The first generic parameter of the abstract class is the type of the Actor, and the second parameter is the type of the message.
[ActorMessageHandler(AppType.Map)]
public class Frame_ClickMapHandler : AMActorLocationHandler<Unit, Frame_ClickMap>
{
protected override ETTask Run(Unit unit, Frame_ClickMap message)
{
Vector3 target = new Vector3(message.X, message.Y, message.Z);
unit.GetComponent<UnitPathComponent>().MoveTo(target).Coroutine();
}
}
// To handle Rpc messages, you need to inherit the AMActorRpcHandler abstract class. The first generic parameter of the abstract class is the type of the Actor, the second parameter is the type of the message, and the third parameter is the type of the returned message
[ActorMessageHandler(AppType.Map)]
public class C2M_TestActorRequestHandler : AMActorLocationRpcHandler<Unit, C2M_TestActorRequest, M2C_TestActorResponse>
{
protected override async ETTask Run(Unit unit, C2M_TestActorRequest message, Action<M2C_TestActorResponse> reply)
{
reply(new M2C_TestActorResponse(){Info = "actor rpc response"});
await ETTask.CompletedTask;
}
}
```
### ET's actor and actor location analogy
There are many cities (processes) in China, and many people (entity objects) living in the cities, each with an ID number (Entity.Id). A person needs to apply for a residence permit in each city and is assigned a unique residence permit number (InstanceId). The format of the residence permit number is 2 bytes city number + 4 bytes time + 2 bytes increment. The ID number never changes, but the residence permit number changes every time you go to a city.
Now there is a China Post (actor). Suppose Xiaoming wants to send a letter to his girlfriend Xiaohong
1. Xiaohong must mount a mailbox (MailboxComponent) himself in order to receive the letter, and Xiaohong will process the message when he receives it. Note that processing is done here one by one. It is possible that Hong will receive letters from many people at the same time. But she must read one letter at a time. Let's say Xiaoming and Xiaobao both send letters to Xiaohong, and Xiaohong receives Xiaoming's letter first, and then Xiaobao's letter. Xiaohong reads Xiaoming's letter first, and Xiaoming's letter asks Xiaohong to make a phone call to her grandmother (to produce a concordance) and then write back to herself, noting that Xiaohong also cannot read the next letter during this period, and must finish the phone call before she can read Xiaobao's letter. Of course Xiao Hong himself can choose to start reading Xiao Bao's letter without finishing the process, by opening a new concatenation for Xiao Ming's letter.
2. Suppose Xiaoming knows Xiaohong's residence permit number, then the post (actor) can find the city (process) where Xiaohong lives according to the two docks of the residence permit number, and then find Xiaohong and deliver the message to Xiaohong's mailbox (MailboxComponent) according to Xiaohong's residence permit number. This is the simplest native actor model
3. ET also supports a set of actor location mechanism. Suppose Xiao Ming doesn't know Xiao Hong's residence permit number, but he knows Xiao Hong's ID number, what should he do? The postal service has developed a set of advanced post (actor location) to think of a way, if a person often moves, it still wants to receive letters, then he must report his residence permit and ID card to the central government (location server) when he goes to a new city, so that the advanced post can send mail through the ID card number. The method is to go to the central government to get Hong's residence permit number, and then use the actor mechanism to send. 4.
4. Suppose Xiao Hong was in Guangzhou city before, and Xiao Ming used Xiao Hong's ID card to send a letter to Xiao Hong. Advanced post obtains Xiao Hong's residence permit number and sends a letter to Xiao Hong. During this process of sending the letter, Xiaohong moved, from Guangzhou to Shenzhen, at which time Xiaohong reported her new residence permit on the central government. When the letter from the senior post arrives in Guangzhou, it is found that Xiao Hong is not in Guangzhou. Then the senior postal service will go to the central government again to get Xiao Hong's residence permit and resend it, which may succeed or fail again, this process will be repeated several times, if it is unsuccessful, it will tell Xiao Ming that the letter has failed to be sent.
5. senior postal mail is more expensive, and people do not move a lot, usually Xiao Ming will remember Xiao Hong's residence permit after sending letters with senior postal mail, and next time send letters directly with the residence permit, and then send letters with senior postal mail if it fails.
There are two kinds of return receipts, one without content, just means Xiao Hong received the letter, and one with Xiao Hong's return letter. Xiaoming can choose which return receipt form to use when he sends the letter. Xiao Ming cannot send two letters to Xiao Hong at the same time, he must wait for Xiao Hong's acknowledgement to arrive before Xiao Ming can continue sending the letter.
================================================
FILE: Book/5.5Actor Location-ZH.md
================================================
# Actor Location
### Actor Location
Actor模型只需要知道对方的InstanceId就能发送消息,十分方便,但是有时候我们可能无法知道对方的InstanceId,或者是一个Actor的InstanceId会发生变化。这种场景很常见,比如:很多游戏是分线的,一个玩家可能从1线换到2线,还有的游戏是分场景的,一个场景一个进程,玩家从场景1进入到场景2。因为做了进程迁移,玩家对象的InstanceId也就变化了。ET提供了给这类对象发送消息的机制,叫做Actor Location机制。其原理比较简单:
1. 因为InstanceId是变化的,对象的Entity.Id是不变的,所以我们首先可以想到使用Entity.Id来发送actor消息
2. 提供一个位置进程(Location Server),Actor对象可以将自己的Entity.Id跟InstanceId作为kv存到位置进程中。发送Actor消息前先去位置进程查询到Actor对象的InstanceId再发送actor消息。
3. Actor对象在一个进程创建时或者迁移到一个新的进程时,都需要把自己的Id跟InstanceId注册到Location Server上去
4. 因为Actor对象是可以迁移的,消息发过去有可能Actor已经迁移到其它进程上去了,所以发送Actor Location消息需要提供一种可靠机制
5. ActorLocationSender提供两种方法,Send跟Call,Send一个消息也需要接受者返回一个消息,只有收到返回消息才会发送下一个消息。
6. Actor对象如果迁移走了,这时会返回Actor不存在的错误,发送者收到这个错误会等待1秒,然后重新去获取Actor的InstanceId,然后重新发送,目前会尝试5次,5次过后,抛出异常,报告错误
7. ActorLocationSender发送消息不会每次都去查询Location Server,因为对象迁移毕竟比较少见,只有第一次去查询,之后缓存InstanceId,以后发送失败再重新查询。
8. Actor对象在迁移过程中,有可能其它进程发送过来消息,这时会发生错误,所以location server提供了一种Lock的机制。对象在传送前,删掉在本进程的信息,然后在location server上加上锁,一旦锁上后,其它的对该key的请求会进行队列。
9. 传送前因为对方删除了本进程的actor,所以其它进程会发送失败,这时候他们会进行重试。重试的时候会重新请求location server,这时候会发现被锁了,于是一直等待
10. 传送完成后,要unlock location server上的锁,并且更新新的地址,然后响应其它的location请求。其它发给这个actor的请求继续进行下去。
注意,Actor模型是纯粹的服务端消息通信机制,跟客户端是没什么关系的,很多用ET的新人看到ET客户端消息也有Actor接口,以为这是客户端跟服务端通信的机制,其实不是的。ET客户端使用这个Actor完全是因为Gate需要对客户端消息进行转发,我们可以正好利用服务端actor模型来进行转发,所以客户端有些消息也是继承了actor的接口。假如我们客户端不使用actor接口会怎么样呢?比如,Frame_ClickMap这个消息
```protobuf
message Frame_ClickMap // IActorLocationMessage
{
int64 ActorId = 93;
int64 Id = 94;
float X = 1;
float Y = 2;
float Z = 3;
}
```
我们可能就不需要ActorId这个字段,消息发送到Gate,gate看到是Frame_ClickMap消息,它需要转发给Map上的Unit,转发还好办,gate可以从session中获取对应的map的unit的位置,然后转发,问题来了,Frame_ClickMap消息到了map,map怎么知道消息需要给哪个对象呢?这时候有几种设计:
1. 在转发的底层协议中带上unit的Id,需要比较复杂的底层协议支持。
2. 用一个消息对Frame_ClickMap消息包装一下,包装的消息带上Unit的Id,用消息包装意味着更大的消耗,增加GC。
个人感觉这两种都很差,不好用,而且就算分发给unit对象处理了,怎么解决消息重入的问题呢?unit对象仍然需要挂上一个消息处理队列,然后收到消息扔到队列里面。这不跟actor模型重复了吗?目前ET在客户端发给unit的消息做了个设计,消息做成actor消息,gate收到发现是actor消息,直接发到对应的actor上,解决的可以说很漂亮。其实客户端仍然是使用session.send跟call发送消息,发送的时候也不知道消息是actor消息,只有到了gate,gate才进行了判断,参考OuterMessageDispatcher.cs
### Actor Location消息的处理
ActorLocation消息发送
```csharp
// 从Game.Scene上获取ActorLocationSenderComponent,然后通过Entity.Id获取ActorLocationSender
ActorLocationSender actorLocationSender = Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
// 通过ActorLocationSender来发送消息
actorLocationSender.Send(actorLocationMessage);
// 发送Rpc消息
IResponse response = await actorLocationSender.Call(actorLocationRequest);
```
ActorLocation消息的处理跟Actor消息几乎一样,不同的是继承的两个抽象类不同,注意actorlocation的抽象类多了个Location
```csharp
// 处理send过来的消息, 需要继承AMActorLocationHandler抽象类,抽象类第一个泛型参数是Actor的类型,第二个参数是消息的类型
[ActorMessageHandler(AppType.Map)]
public class Frame_ClickMapHandler : AMActorLocationHandler<Unit, Frame_ClickMap>
{
protected override ETTask Run(Unit unit, Frame_ClickMap message)
{
Vector3 target = new Vector3(message.X, message.Y, message.Z);
unit.GetComponent<UnitPathComponent>().MoveTo(target).Coroutine();
}
}
// 处理Rpc消息, 需要继承AMActorRpcHandler抽象类,抽象类第一个泛型参数是Actor的类型,第二个参数是消息的类型,第三个参数是返回消息的类型
[ActorMessageHandler(AppType.Map)]
public class C2M_TestActorRequestHandler : AMActorLocationRpcHandler<Unit, C2M_TestActorRequest, M2C_TestActorResponse>
{
protected override async ETTask Run(Unit unit, C2M_TestActorRequest message, Action<M2C_TestActorResponse> reply)
{
reply(new M2C_TestActorResponse(){Info = "actor rpc response"});
await ETTask.CompletedTask;
}
}
```
### ET的actor跟actor location的比喻
中国有很多城市(进程),城市中有很多人(entity对象)居住,每个人都有身份证号码(Entity.Id)。一个人每到一个市都需要办理居住证,分配到唯一的居住证号码(InstanceId),居住证号码的格式是2个字节市编号+4个字节时间+2个字节递增。身份证号码是永远不会变化的,但是居住证号码每到一个城市都变化的。
现在有个中国邮政(actor)。假设小明要发信给女朋友小红
1. 小红为了收信,自己必须挂载一个邮箱(MailboxComponent),小红收到消息就会处理。注意这里处理是一个个进行处理的。有可能小红会同时收到很多人的信。但是她必须一封一封的信看,比方说小明跟小宝都发了信给小红,小红先收到小明的信,再收到了小宝的信。小红先读小明的信,小明信中让小红给外婆打个电话(产生协程)再给自己回信,注意这期间小红也不能读下一封信,必须打完电话后才能读小宝的信。当然小红自己可以选择不处理完成就开始读小宝的信,做法是小红开一个新的协程来处理小明的信。
2. 假设小明知道小红的居住证号码,那么邮政(actor)可以根据居住证号码头两位找到小红居住的城市(进程),然后再根据小红的居住证编号,找到小红,把消息投递到小红的邮箱(MailboxComponent)中。这种是最简单的原生的actor模型
3. ET还支持了一套actor location机制。假设小明不知道小红的居住证号码,但是他知道小红的身份证号码,怎么办呢?邮政开发了一套高级邮政(actor location)想了一个办法,如果一个人经常搬家,它还想收到信,那他到一个新的城市都必须把自己的居住证跟身份证上报到中央政府(location server),这样高级邮政能够通过身份证号码来发送邮件。方法就是去中央政府拿到小红的居住证号码,再利用actor机制发送。
4. 假设小红之前在广州市,小明用小红的身份证给小红发信件了。 高级邮政获取了小红的居住证号码,给小红发信。发信的这个过程中,小红搬家了,从广州搬到了深圳,这时小红在中央政府上报了自己新的居住证。 高级邮政的信送到到广州的时候发现,小红不在广州。那么高级邮政会再次去中央政府获取小红的居住证,重新发送,有可能成功有可能再次失败,这个过程会重复几次,如果一直不成功则告诉小明,信件发送失败了。
5. 高级邮政发信比较贵,而且人搬家的次数并不多,一般小明用高级邮政发信后会记住小红的居住证,下次再发的时候直接用居住证发信,发送失败了再使用高级邮政发信。
6. 高级邮政的信都是有回执的,有两种回执,一种回执没有内容,只表示小红收到了信,一种回执带了小红的回信。小明在发信的时候可以选择使用哪种回执形式。小明给小红不能同时发送两封信,必须等小红的回执到了,小明才能继续发信。
================================================
FILE: Book/5.6Numerical component design.md
================================================
Similar to world of warcraft, moba such skills are extremely complex, flexibility requires a very high skill system, must need a set of its flexible numerical structure to match. Numerical structure is well designed, the realization of the skill system will be very simple, otherwise it is a disaster. For example, in World of Warcraft, a character has many numerical attributes, such as movement speed, strength, anger, energy, concentration value, magic value, blood, maximum blood, physical attack, physical defense, spell attack, spell defense, etc. There are dozens of attributes. Attributes and attributes affect each other, buffs will add absolute value to attributes, increase the percentage, or some kind of buff will come back to you after counting all the increased value and doubling it.
## Common practice:
The general is to write a value class.
```c#
class Numeric
{
public int Hp;
public int MaxHp;
public int Speed;
// Energy
public int Energy;
public int MaxEnergy;
// Magic
public int Mp;
public int MaxMp;
.....
}
```
On second thought, I'm a thief using energy why should I have a value of Mp? I am a mage using magic why should there be a field for energy? I'm not sure what to do with this, just pretend you didn't see it? I can not, I come to an inheritance?
```C#
// Mage values
calss MageNumeric: Numeric
{
// magic
public int Mp;
public int MaxMp;
}
// Thief value
calss RougeNumeric: Numeric
{
// Energy
public int Energy;
public int MaxEnergy;
}
````
10 races, each race 7, 8 kinds of heroes, just these values class inheritance relationship, you have to be confused it. Object-oriented is difficult to adapt to the needs of this flexible and complex.
And look at the Numeric class, each value can not just design a field, for example, I have a buff will increase 10 points Speed, and a kind of buff to increase 50% of the speed, then I must add at least three secondary attribute fields
```c#
class Numeric
{
// speed final value
public int Speed;
// Speed initial value
public int SpeedInit;
// Speed increase value
public int SpeedAdd;
// Speed increase percentage value
public int SpeedPct;
}
```
After SpeedAdd and SpeedPct are changed, a calculation is performed to calculate the final speed value. buff only needs to go to modify SpeedAdd and SpeedPct on the line.
```c#
Speed = (SpeedInit + SpeedAdd) * (100 + SpeedPct) / 100
```
Each property may have several indirect effects on the value, you can think about how large this class is, a rough estimate of more than 100 fields. The trouble is that the formula is basically the same, but just can not be unified into a function, such as MaxHp, also has a buff effect
```c#
class Numeric
{
public int Speed;
public int SpeedInit;
public int SpeedAdd;
public int SpeedPct;
public int MaxHp;
public int MaxHpInit;
public int MaxHpAdd;
public int MaxHpPct;
}
```
Also have to write a formula for calculating Hp
```c#
MaxHp = (MaxHpInit + MaxHpAdd) * (100 + MaxHpPct) / 100
```
Dozens of properties, you have to write dozens of times, and each secondary property changes to correctly call the corresponding formula calculation. Very troublesome!
This design also has a big problem, buff configuration table to fill the corresponding attribute field is not very good to fill, for example, sprint buff (increase speed 50%), how to configure the buff table to make the program simple to find and operate the SpeedPct field? Not a good idea.
## ET framework uses the Key Value form to save the value of the property
```c#
Using System.Collections.Generic;
Generic; namespace Model
Generic; namespace Model {
public enum NumericType
{
Max = 10000,
Speed = 1000,
SpeedBase = Speed * 10 + 1,
SpeedAdd = Speed * 10 + 2,
SpeedPct = Speed * 10 + 3,
SpeedFinalAdd = Speed * 10 + 4,
SpeedFinalPct = Speed * 10 + 5,
Hp = 1001,
HpBase = Hp * 10 + 1,
MaxHp = 1002,
MaxHpBase = MaxHp * 10 + 1,
MaxHpAdd = MaxHp * 10 + 2,
MaxHpPct = MaxHp * 10 + 3,
MaxHpFinalAdd = MaxHp * 10 + 4,
MaxHpFinalPct = MaxHp * 10 + 5,
}
public class NumericComponent: Component
{
public readonly Dictionary<int, int> NumericDic = new Dictionary<int, int>();
public void Awake()
{
// initialize base value here
}
public float GetAsFloat(NumericType numericType)
{
return (float)GetByKey((int)numericType) / 10000;
}
public int GetAsInt(NumericType numericType)
{
return GetByKey((int)numericType);
}
public void Set(NumericType nt, float value)
{
this[nt] = (int) (value * 10000);
}
public void Set(NumericType nt, int value)
{
this[nt] = value;
}
public int this[NumericType numericType]
{
get
{
return this.GetByKey((int) numericType);
}
set
{
int v = this.GetByKey((int) numericType);
if (v == value)
{
return;
}
NumericDic[(int)numericType] = value;
Update(numericType);
}
}
private int GetByKey(int key)
{
int value = 0;
This.NumericDic.TryGetValue(key, out value);
return value;
}
public void Update(NumericType numericType)
{
if (numericType > NumericType.Max)
{
return;
}
int final = (int) numericType / 10;
int bas = final * 10 + 1;
int add = final * 10 + 2;
int pct = final * 10 + 3;
int finalAdd = final * 10 + 4;
int finalPct = final * 10 + 5;
// A value may be affected by a variety of circumstances, such as speed, adding a buff may increase the speed of the absolute value of 100, but also some buffs increase the speed of 10%, so a value can be controlled by 5 values of the final result
// final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;
this.NumericDic[final] = ((this.GetByKey(base) + this.GetByKey(add)) * (100 + this.GetByKey(pct)) / 100 + this.GetByKey(finalAdd)) * (100 + this. GetByKey(finalPct)) / 100;
Game.EventSystem.Run(EventIdType.NumbericChange, this.Entity.Id, numericType, final);
}
}
}
```
1. values are saved with key value, key is the type of value, defined by NumericType, value are integers, float type can also be converted to integers, for example, multiply by 1000; key value to save properties will become very flexible, for example, mage no energy properties, then initialize the mage object does not add energy key value It's fine. Thieves do not have mana, no spell damage, etc., the initialization will not need to add these.
2. world of warcraft, a value by 5 values to influence, you can unify the use of a formula.
```
final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;
```
For example, the speed value speed, there is an initial value speedbase, there is a buff1 to increase the absolute speed by 10 points, then buff1 will add 10 to speedadd when it is created, buff1 minus 10 to speedadd when it is deleted, buff2 increases the speed by 20%, then buff2 adds to speedpct when it is created The 5 values are changed and the corresponding properties can be recalculated by using the Update function in a unified way. buff configuration is quite simple. If the corresponding NumericType is filled in the buff configuration, the program can easily manipulate the corresponding value.
3. Changes in properties can be uniformly thrown to other m
gitextract_fkscp4eu/ ├── .gitattributes ├── .gitignore ├── Assets/ │ ├── .gitignore │ ├── DefaultVolumeProfile.asset │ ├── DefaultVolumeProfile.asset.meta │ ├── Resources/ │ │ ├── BuildinFileManifest.asset │ │ └── BuildinFileManifest.asset.meta │ ├── Resources.meta │ ├── Settings/ │ │ ├── Build Profiles/ │ │ │ ├── Mac.asset │ │ │ └── Mac.asset.meta │ │ └── Build Profiles.meta │ ├── Settings.meta │ ├── link.xml │ └── link.xml.meta ├── Book/ │ ├── 1.1运行指南.md │ ├── 1.2Why use .net core.md │ ├── 1.2为什么使用.net core.md │ ├── 2.1CSharp Coroutine.md │ ├── 2.1CSharp的协程.md │ ├── 2.2Better Coroutine.md │ ├── 2.2更好的协程.md │ ├── 2.3Single-threaded asynchronous.md │ ├── 2.3单线程异步.md │ ├── 3.2The powerful MongoBson library.md │ ├── 3.2强大的MongoBson库.md │ ├── 3.3Everything is Entity.md │ ├── 3.3一切皆实体.md │ ├── 3.4EventSystem.md │ ├── 3.4事件机制EventSystem.md │ ├── 4.1Component-based design.md │ ├── 4.1组件式设计.md │ ├── 5.4Actor Model.md │ ├── 5.4Actor模型.md │ ├── 5.5Actor Location-EN.md │ ├── 5.5Actor Location-ZH.md │ ├── 5.6Numerical component design.md │ ├── 5.6数值组件设计.md │ ├── 6.1AI Framwork.md │ ├── 6.1AI框架.md │ ├── 6.2AI框架-行为机.md │ ├── 7.1代码规范.md │ ├── 8.1ET Package制作指南.md │ ├── 8.2ET Package目录.md │ └── 8.3ET9项目怎么进行包更新.md ├── ChangeLog.md ├── Directory.Build.props ├── ET.sln.DotSettings ├── LICENSE ├── Packages/ │ ├── .gitignore │ ├── com.etetet.init/ │ │ ├── .gitignore │ │ ├── Editor/ │ │ │ ├── ET.Init.Editor.asmdef │ │ │ ├── ET.Init.Editor.asmdef.meta │ │ │ ├── GitDependencyResolver/ │ │ │ │ ├── DependencyResolver.cs │ │ │ │ └── DependencyResolver.cs.meta │ │ │ ├── GitDependencyResolver.meta │ │ │ ├── PackageGit.cs │ │ │ ├── PackageGit.cs.meta │ │ │ ├── ProcessHelper.cs │ │ │ └── ProcessHelper.cs.meta │ │ ├── Editor.meta │ │ ├── MoveToPackages.ps1 │ │ ├── MoveToPackages.ps1.meta │ │ ├── MoveToPackages_6.ps1 │ │ ├── MoveToPackages_6.ps1.meta │ │ ├── Plugins/ │ │ │ └── MongoDB.meta │ │ ├── Plugins.meta │ │ ├── package.json │ │ └── package.json.meta │ ├── com.halodi.halodi-unity-package-registry-manager/ │ │ ├── CHANGELOG.md │ │ ├── CHANGELOG.md.meta │ │ ├── Editor/ │ │ │ ├── Halodi/ │ │ │ │ ├── PackageRegistry/ │ │ │ │ │ ├── Core/ │ │ │ │ │ │ ├── CredentialManager.cs │ │ │ │ │ │ ├── CredentialManager.cs.meta │ │ │ │ │ │ ├── RegistryManager.cs │ │ │ │ │ │ ├── RegistryManager.cs.meta │ │ │ │ │ │ ├── ScopedRegistry.cs │ │ │ │ │ │ ├── ScopedRegistry.cs.meta │ │ │ │ │ │ ├── UpgradePackagesManager.cs │ │ │ │ │ │ └── UpgradePackagesManager.cs.meta │ │ │ │ │ ├── Core.meta │ │ │ │ │ ├── NPM/ │ │ │ │ │ │ ├── NPMLogin.cs │ │ │ │ │ │ ├── NPMLogin.cs.meta │ │ │ │ │ │ ├── NPMPublish.cs │ │ │ │ │ │ ├── NPMPublish.cs.meta │ │ │ │ │ │ ├── NPMResponse.cs │ │ │ │ │ │ ├── NPMResponse.cs.meta │ │ │ │ │ │ ├── PackageTarball.cs │ │ │ │ │ │ ├── PackageTarball.cs.meta │ │ │ │ │ │ ├── PublicationManifest.cs │ │ │ │ │ │ ├── PublicationManifest.cs.meta │ │ │ │ │ │ ├── WebExceptionParser.cs │ │ │ │ │ │ └── WebExceptionParser.cs.meta │ │ │ │ │ ├── NPM.meta │ │ │ │ │ ├── UI/ │ │ │ │ │ │ ├── BulkAddPackages.cs │ │ │ │ │ │ ├── BulkAddPackages.cs.meta │ │ │ │ │ │ ├── CredentialEditorView.cs │ │ │ │ │ │ ├── CredentialEditorView.cs.meta │ │ │ │ │ │ ├── CredentialManagerView.cs │ │ │ │ │ │ ├── CredentialManagerView.cs.meta │ │ │ │ │ │ ├── GetTokenView.cs │ │ │ │ │ │ ├── GetTokenView.cs.meta │ │ │ │ │ │ ├── InstallPackageCreator.cs │ │ │ │ │ │ ├── InstallPackageCreator.cs.meta │ │ │ │ │ │ ├── RegistryManagerView.cs │ │ │ │ │ │ ├── RegistryManagerView.cs.meta │ │ │ │ │ │ ├── ScopedRegistryEditorView.cs │ │ │ │ │ │ ├── ScopedRegistryEditorView.cs.meta │ │ │ │ │ │ ├── SettingsProvider.cs │ │ │ │ │ │ ├── SettingsProvider.cs.meta │ │ │ │ │ │ ├── UpgradePackagesView.cs │ │ │ │ │ │ └── UpgradePackagesView.cs.meta │ │ │ │ │ └── UI.meta │ │ │ │ └── PackageRegistry.meta │ │ │ ├── Halodi.PackageRegistryManager.Editor.asmdef │ │ │ ├── Halodi.PackageRegistryManager.Editor.asmdef.meta │ │ │ ├── Halodi.meta │ │ │ ├── ThirdParty/ │ │ │ │ ├── LICENSE.Tomlyn.md │ │ │ │ ├── LICENSE.Tomlyn.md.meta │ │ │ │ ├── README.md │ │ │ │ ├── README.md.meta │ │ │ │ ├── Tomlyn.dll.meta │ │ │ │ ├── Unity-SemVer/ │ │ │ │ │ ├── Artees.UnitySemVer.asmdef │ │ │ │ │ ├── Artees.UnitySemVer.asmdef.meta │ │ │ │ │ ├── CloudBuildManifest.cs │ │ │ │ │ ├── CloudBuildManifest.cs.meta │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── LICENSE.meta │ │ │ │ │ ├── README.md │ │ │ │ │ ├── README.md.meta │ │ │ │ │ ├── SemVer.cs │ │ │ │ │ ├── SemVer.cs.meta │ │ │ │ │ ├── SemVerAttribute.cs │ │ │ │ │ ├── SemVerAttribute.cs.meta │ │ │ │ │ ├── SemVerAutoBuild.cs │ │ │ │ │ ├── SemVerAutoBuild.cs.meta │ │ │ │ │ ├── SemVerComparer.cs │ │ │ │ │ ├── SemVerComparer.cs.meta │ │ │ │ │ ├── SemVerConverter.cs │ │ │ │ │ ├── SemVerConverter.cs.meta │ │ │ │ │ ├── SemVerErrorMessage.cs │ │ │ │ │ ├── SemVerErrorMessage.cs.meta │ │ │ │ │ ├── SemVerValidationResult.cs │ │ │ │ │ ├── SemVerValidationResult.cs.meta │ │ │ │ │ ├── SemVerValidator.cs │ │ │ │ │ └── SemVerValidator.cs.meta │ │ │ │ └── Unity-SemVer.meta │ │ │ └── ThirdParty.meta │ │ ├── Editor.meta │ │ ├── LICENSE.md │ │ ├── LICENSE.md.meta │ │ ├── README.md │ │ ├── README.md.meta │ │ ├── Tests/ │ │ │ ├── Editor/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ ├── AssemblyInfo.cs.meta │ │ │ │ ├── Halodi.PackageRegistryManager.Tests.asmdef │ │ │ │ └── Halodi.PackageRegistryManager.Tests.asmdef.meta │ │ │ └── Editor.meta │ │ ├── Tests.meta │ │ ├── package.json │ │ └── package.json.meta │ ├── com.unity.ide.rider/ │ │ ├── .editorconfig │ │ ├── .signature │ │ ├── CHANGELOG.md │ │ ├── CHANGELOG.md.meta │ │ ├── CONTRIBUTING.md │ │ ├── CONTRIBUTING.md.meta │ │ ├── Documentation~/ │ │ │ ├── README.md │ │ │ ├── TableOfContents.md │ │ │ ├── index.md │ │ │ └── using-the-jetbrains-rider-editor-package.md │ │ ├── LICENSE.md │ │ ├── LICENSE.md.meta │ │ ├── Rider/ │ │ │ ├── Editor/ │ │ │ │ ├── Discovery.cs │ │ │ │ ├── Discovery.cs.meta │ │ │ │ ├── EditorPluginInterop.cs │ │ │ │ ├── EditorPluginInterop.cs.meta │ │ │ │ ├── JetBrains.Rider.PathLocator.dll.meta │ │ │ │ ├── LoggingLevel.cs │ │ │ │ ├── LoggingLevel.cs.meta │ │ │ │ ├── PluginSettings.cs │ │ │ │ ├── PluginSettings.cs.meta │ │ │ │ ├── PostProcessors/ │ │ │ │ │ ├── RiderAssetPostprocessor.cs │ │ │ │ │ └── RiderAssetPostprocessor.cs.meta │ │ │ │ ├── PostProcessors.meta │ │ │ │ ├── ProjectGeneration/ │ │ │ │ │ ├── AssemblyNameProvider.cs │ │ │ │ │ ├── AssemblyNameProvider.cs.meta │ │ │ │ │ ├── FileIOProvider.cs │ │ │ │ │ ├── FileIOProvider.cs.meta │ │ │ │ │ ├── GUIDProvider.cs │ │ │ │ │ ├── GUIDProvider.cs.meta │ │ │ │ │ ├── IAssemblyNameProvider.cs │ │ │ │ │ ├── IAssemblyNameProvider.cs.meta │ │ │ │ │ ├── IFileIO.cs │ │ │ │ │ ├── IFileIO.cs.meta │ │ │ │ │ ├── IGUIDGenerator.cs │ │ │ │ │ ├── IGUIDGenerator.cs.meta │ │ │ │ │ ├── IGenerator.cs │ │ │ │ │ ├── IGenerator.cs.meta │ │ │ │ │ ├── LastWriteTracker.cs │ │ │ │ │ ├── LastWriteTracker.cs.meta │ │ │ │ │ ├── PackageManagerTracker.cs │ │ │ │ │ ├── PackageManagerTracker.cs.meta │ │ │ │ │ ├── ProjectGeneration.cs │ │ │ │ │ ├── ProjectGeneration.cs.meta │ │ │ │ │ ├── ProjectGenerationFlag.cs │ │ │ │ │ ├── ProjectGenerationFlag.cs.meta │ │ │ │ │ ├── ProjectPart.cs │ │ │ │ │ ├── ProjectPart.cs.meta │ │ │ │ │ ├── SolutionGuidGenerator.cs │ │ │ │ │ └── SolutionGuidGenerator.cs.meta │ │ │ │ ├── ProjectGeneration.meta │ │ │ │ ├── Properties/ │ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ │ └── AssemblyInfo.cs.meta │ │ │ │ ├── Properties.meta │ │ │ │ ├── RiderInitializer.cs │ │ │ │ ├── RiderInitializer.cs.meta │ │ │ │ ├── RiderScriptEditor.cs │ │ │ │ ├── RiderScriptEditor.cs.meta │ │ │ │ ├── RiderScriptEditorData.cs │ │ │ │ ├── RiderScriptEditorData.cs.meta │ │ │ │ ├── RiderScriptEditorDataPersisted.cs │ │ │ │ ├── RiderScriptEditorDataPersisted.cs.meta │ │ │ │ ├── RiderStyles.cs │ │ │ │ ├── RiderStyles.cs.meta │ │ │ │ ├── StartUpMethodExecutor.cs │ │ │ │ ├── StartUpMethodExecutor.cs.meta │ │ │ │ ├── UnitTesting/ │ │ │ │ │ ├── CallbackData.cs │ │ │ │ │ ├── CallbackData.cs.meta │ │ │ │ │ ├── CallbackInitializer.cs │ │ │ │ │ ├── CallbackInitializer.cs.meta │ │ │ │ │ ├── RiderTestRunner.cs │ │ │ │ │ ├── RiderTestRunner.cs.meta │ │ │ │ │ ├── SyncTestRunCallback.cs │ │ │ │ │ ├── SyncTestRunCallback.cs.meta │ │ │ │ │ ├── SyncTestRunEventsHandler.cs │ │ │ │ │ ├── SyncTestRunEventsHandler.cs.meta │ │ │ │ │ ├── TestEvent.cs │ │ │ │ │ ├── TestEvent.cs.meta │ │ │ │ │ ├── TestsCallback.cs │ │ │ │ │ └── TestsCallback.cs.meta │ │ │ │ ├── UnitTesting.meta │ │ │ │ ├── Util/ │ │ │ │ │ ├── CommandLineParser.cs │ │ │ │ │ ├── CommandLineParser.cs.meta │ │ │ │ │ ├── FileSystemUtil.cs │ │ │ │ │ ├── FileSystemUtil.cs.meta │ │ │ │ │ ├── LibcNativeInterop.cs │ │ │ │ │ ├── LibcNativeInterop.cs.meta │ │ │ │ │ ├── RiderMenu.cs │ │ │ │ │ ├── RiderMenu.cs.meta │ │ │ │ │ ├── RiderPathUtil.cs │ │ │ │ │ ├── RiderPathUtil.cs.meta │ │ │ │ │ ├── SerializableVersion.cs │ │ │ │ │ ├── SerializableVersion.cs.meta │ │ │ │ │ ├── StringBuilderExtensions.cs │ │ │ │ │ ├── StringBuilderExtensions.cs.meta │ │ │ │ │ ├── StringUtils.cs │ │ │ │ │ └── StringUtils.cs.meta │ │ │ │ ├── Util.meta │ │ │ │ ├── com.unity.ide.rider.asmdef │ │ │ │ └── com.unity.ide.rider.asmdef.meta │ │ │ └── Editor.meta │ │ ├── Rider.meta │ │ ├── package.json │ │ └── package.json.meta │ ├── manifest.json │ └── packages-lock.json ├── ProjectSettings/ │ ├── AudioManager.asset │ ├── AutoStreamingSettings.asset │ ├── BurstAotSettings_Android.json │ ├── BurstAotSettings_StandaloneOSX.json │ ├── BurstAotSettings_StandaloneWindows.json │ ├── BurstAotSettings_iOS.json │ ├── ClusterInputManager.asset │ ├── CommonBurstAotSettings.json │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── HybridCLRSettings.asset │ ├── InputManager.asset │ ├── MemorySettings.asset │ ├── MultiplayerManager.asset │ ├── NavMeshAreas.asset │ ├── NavMeshLayers.asset │ ├── NetworkManager.asset │ ├── PackageManagerSettings.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── SceneTemplateSettings.json │ ├── ShaderGraphSettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── TimelineSettings.asset │ ├── URPProjectSettings.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ ├── VersionControlSettings.asset │ └── XRSettings.asset ├── README.md ├── Scripts/ │ └── Publish-linux-x64.ps1 ├── Unity.sln.DotSettings └── Unity.userprefs
SYMBOL INDEX (400 symbols across 70 files)
FILE: Packages/com.etetet.init/Editor/GitDependencyResolver/DependencyResolver.cs
class DependencyResolver (line 17) | [InitializeOnLoad]
method MoveToPackage (line 21) | static void MoveToPackage(string package, string version)
method DependencyResolver (line 43) | static DependencyResolver()
method OnPackagesRegistered (line 49) | static void OnPackagesRegistered(PackageRegistrationEventArgs packageR...
method RepairDependencies (line 82) | [MenuItem("ET/Init/RepairDependencies")]
FILE: Packages/com.etetet.init/Editor/PackageGit.cs
class PackageGit (line 9) | [BsonIgnoreExtraElements]
class PackageGitHelper (line 18) | public static class PackageGitHelper
method Load (line 20) | public static PackageGit Load(string packageJsonPath)
FILE: Packages/com.etetet.init/Editor/ProcessHelper.cs
class ProcessHelper (line 10) | internal static class ProcessHelper
method PowerShell (line 12) | public static System.Diagnostics.Process PowerShell(string arguments, ...
method Run (line 24) | public static System.Diagnostics.Process Run(string exe, string argume...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/CredentialManager.cs
class NPMCredential (line 13) | public class NPMCredential
class CredentialManager (line 20) | public class CredentialManager
method CredentialManager (line 48) | public CredentialManager()
method Write (line 81) | public void Write()
method HasRegistry (line 107) | public bool HasRegistry(string url)
method GetCredential (line 112) | public NPMCredential GetCredential(string url)
method SetCredential (line 117) | public void SetCredential(string url, bool alwaysAuth, string token)
method RemoveCredential (line 137) | public void RemoveCredential(string url)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/RegistryManager.cs
class RegistryManager (line 10) | public class RegistryManager
method RegistryManager (line 25) | public RegistryManager()
method LoadRegistry (line 46) | private ScopedRegistry LoadRegistry(JObject Jregistry)
method UpdateScope (line 69) | private void UpdateScope(ScopedRegistry registry, JToken registryEleme...
method GetOrCreateScopedRegistry (line 78) | private JToken GetOrCreateScopedRegistry(ScopedRegistry registry, JObj...
method Remove (line 107) | public void Remove(ScopedRegistry registry)
method Save (line 126) | public void Save(ScopedRegistry registry)
method write (line 146) | private void write(JObject manifestJSON)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/ScopedRegistry.cs
class ScopedRegistry (line 7) | [System.Serializable]
method ToString (line 18) | public override string ToString()
method isValidCredential (line 23) | public bool isValidCredential()
method isValid (line 43) | public bool isValid()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/UpgradePackagesManager.cs
class UpgradePackagesManager (line 12) | public class UpgradePackagesManager
class PackageUpgradeState (line 15) | public class PackageUpgradeState
method PackageUpgradeState (line 17) | public PackageUpgradeState(UnityEditor.PackageManager.PackageInfo info)
method GetCurrentVersion (line 113) | internal string GetCurrentVersion()
method HasNewVersion (line 137) | public bool HasNewVersion(bool showPreviewVersion, bool useVerified)
method GetNewestVersion (line 154) | public string GetNewestVersion(bool showPreviewVersion, bool useVeri...
method UpgradePackagesManager (line 191) | public UpgradePackagesManager()
method Update (line 200) | public void Update()
method UpgradePackage (line 222) | public bool UpgradePackage(String packageWithVersion, ref string error)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMLogin.cs
class NPMLoginRequest (line 11) | [System.Serializable]
class ExpectContinueAware (line 19) | public class ExpectContinueAware : System.Net.WebClient
method GetWebRequest (line 21) | protected override System.Net.WebRequest GetWebRequest(Uri address)
class NPMLogin (line 35) | public class NPMLogin
method UrlCombine (line 37) | internal static string UrlCombine(string start, string more)
method GetBintrayToken (line 51) | public static string GetBintrayToken(string user, string apiKey)
method GetLoginToken (line 56) | public static NPMResponse GetLoginToken(string url, string user, strin...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMPublish.cs
class NPMPublish (line 10) | public class NPMPublish
method Publish (line 12) | public static void Publish(string packageFolder, string registry)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMResponse.cs
class NPMResponse (line 3) | [System.Serializable]
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/PackageTarball.cs
class PackageTarball (line 13) | public class PackageTarball
method Create (line 15) | public static string Create(string packageFolder, string outputFolder)
method AppendDirectorySeparatorChar (line 40) | private static string AppendDirectorySeparatorChar(string path)
method AddDirectoryFilesToTar (line 54) | private static void AddDirectoryFilesToTar(TarArchive tarArchive, stri...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/PublicationManifest.cs
class PublicationManifest (line 20) | internal class PublicationManifest
method LoadManifest (line 42) | internal static JObject LoadManifest(string packageFolder)
method PublicationManifest (line 73) | internal PublicationManifest(string packageFolder, string registry)
method GetReadmeFilename (line 150) | private string GetReadmeFilename(string packageFolder)
method GetReadme (line 165) | private string GetReadme(string readmeFile)
method SHA512 (line 170) | private string SHA512(byte[] data)
method SHA1 (line 176) | private string SHA1(byte[] data)
method CreateTarball (line 185) | public void CreateTarball(string packageFolder)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/WebExceptionParser.cs
class WebExceptionParser (line 5) | public class WebExceptionParser
method ParseWebException (line 7) | public static string ParseWebException(WebException e)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/BulkAddPackages.cs
class BulkAddPackages (line 11) | public class BulkAddPackages : EditorWindow
method ManageRegistries (line 16) | internal static void ManageRegistries()
method OnEnable (line 21) | void OnEnable()
method OnGUI (line 26) | void OnGUI()
method AddPackages (line 56) | private void AddPackages()
method CloseWindow (line 99) | private void CloseWindow()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/CredentialEditorView.cs
class CredentialEditorView (line 9) | class CredentialEditorView : EditorWindow
method OnEnable (line 22) | void OnEnable()
method OnDisable (line 28) | void OnDisable()
method CreateNew (line 33) | public void CreateNew(CredentialManager credentialManager)
method Edit (line 41) | public void Edit(NPMCredential credential, CredentialManager credentia...
method OnGUI (line 53) | void OnGUI()
method Save (line 122) | private void Save()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/CredentialManagerView.cs
class CredentialManagerView (line 11) | public class CredentialManagerView : EditorWindow
method OnEnable (line 16) | void OnEnable()
method OnGUI (line 22) | void OnGUI()
method GetCredentialList (line 27) | internal static ReorderableList GetCredentialList(CredentialManager cr...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/GetTokenView.cs
class TokenMethod (line 9) | internal class TokenMethod : GUIContent
method TokenMethod (line 16) | public TokenMethod(string name, string usernameName, string passwordNa...
class GetTokenView (line 24) | internal class GetTokenView : EditorWindow
method OnEnable (line 43) | void OnEnable()
method OnDisable (line 48) | void OnDisable()
method SetRegistry (line 53) | private void SetRegistry(TokenMethod tokenMethod, ScopedRegistry regis...
method OnGUI (line 60) | void OnGUI()
method CreateWindow (line 88) | private static void CreateWindow(TokenMethod method, ScopedRegistry re...
method GetNPMLoginToken (line 96) | private static bool GetNPMLoginToken(ScopedRegistry registry, string u...
method GetBintrayToken (line 113) | private static bool GetBintrayToken(ScopedRegistry registry, string us...
method CloseWindow (line 120) | private void CloseWindow()
method CreateGUI (line 132) | internal static int CreateGUI(int selectedIndex, ScopedRegistry registry)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/InstallPackageCreator.cs
class InstallPackageCreatorView (line 9) | internal class InstallPackageCreatorView : EditorWindow
method ManageRegistries (line 12) | internal static void ManageRegistries()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/RegistryManagerView.cs
class RegistryManagerView (line 11) | public class RegistryManagerView : EditorWindow
method ManageRegistries (line 13) | [MenuItem("ET/Init/Manage scoped registries", false, 21)]
method OnEnable (line 21) | void OnEnable()
method OnGUI (line 27) | void OnGUI()
method GetRegistryList (line 33) | internal static ReorderableList GetRegistryList(RegistryManager regist...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/ScopedRegistryEditorView.cs
class ScopedRegistryEditorView (line 10) | class ScopedRegistryEditorView : EditorWindow
method OnEnable (line 21) | void OnEnable()
method OnDisable (line 28) | void OnDisable()
method CreateNew (line 33) | public void CreateNew(RegistryManager controller)
method Edit (line 41) | public void Edit(ScopedRegistry registry, RegistryManager controller)
method OnGUI (line 51) | void OnGUI()
method Save (line 137) | private void Save()
method Add (line 151) | private void Add()
method UpdateCredential (line 166) | private void UpdateCredential()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/SettingsProvider.cs
class CredentialManagerSettingsProvider (line 12) | static class CredentialManagerSettingsProvider
method CreateMyCustomSettingsProvider (line 14) | [SettingsProvider]
class Styles (line 69) | private static class Styles
method Styles (line 73) | static Styles()
method ThirdPartyInfo (line 85) | private static void ThirdPartyInfo()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/UpgradePackagesView.cs
class UpgradePackagesView (line 10) | internal class UpgradePackagesView : EditorWindow
method ManageRegistries (line 13) | internal static void ManageRegistries()
method OnEnable (line 27) | void OnEnable()
method OnDisable (line 35) | void OnDisable()
method Package (line 42) | private void Package(PackageUpgradeState info)
method OnGUI (line 82) | void OnGUI()
method Upgrade (line 144) | private void Upgrade()
method CloseWindow (line 202) | private void CloseWindow()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/CloudBuildManifest.cs
class CloudBuildManifest (line 9) | internal class CloudBuildManifest
method CloudBuildManifest (line 25) | private CloudBuildManifest()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVer.cs
class SemVer (line 9) | [Serializable]
method Parse (line 56) | public static SemVer Parse(string semVer)
method ClampAndroidBundleVersionCode (line 133) | private static uint ClampAndroidBundleVersionCode(uint value, string n...
method SemVer (line 150) | public SemVer()
method IncrementMajor (line 160) | public void IncrementMajor()
method IncrementMinor (line 169) | public void IncrementMinor()
method IncrementPatch (line 178) | public void IncrementPatch()
method Validate (line 188) | public SemVerValidationResult Validate()
method Clone (line 196) | public SemVer Clone()
method CompareTo (line 209) | public int CompareTo(SemVer other)
method Equals (line 214) | public bool Equals(SemVer other)
method Equals (line 221) | public override bool Equals(object obj)
method GetHashCode (line 228) | public override int GetHashCode()
method ToString (line 233) | public override string ToString()
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerAttribute.cs
class SemVerAttribute (line 8) | public class SemVerAttribute : PropertyAttribute
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerAutoBuild.cs
class SemVerAutoBuild (line 10) | public abstract class SemVerAutoBuild
type Type (line 15) | public enum Type
method Get (line 38) | internal abstract string Get(string build);
method Set (line 40) | internal abstract string Set(string build);
class ManualBuild (line 42) | private class ManualBuild : SemVerAutoBuild
method Get (line 44) | internal override string Get(string build)
method Set (line 49) | internal override string Set(string build)
class CloudBuildNumberBuild (line 55) | private class CloudBuildNumberBuild : ReadOnly
method Get (line 57) | internal override string Get(string build)
class ReadOnly (line 65) | public abstract class ReadOnly : SemVerAutoBuild
method Set (line 67) | internal sealed override string Set(string build)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerComparer.cs
class SemVerComparer (line 7) | internal class SemVerComparer : IComparer<SemVer>
method Compare (line 9) | public int Compare(SemVer x, SemVer y)
method ComparePreReleaseVersions (line 22) | private static int ComparePreReleaseVersions(SemVer x, SemVer y)
method IsPreRelease (line 47) | private static bool IsPreRelease(SemVer semVer)
method ComparePreReleaseIdentifiers (line 52) | private static int ComparePreReleaseIdentifiers(string xIdentifier, st...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerConverter.cs
class SemVerConverter (line 3) | internal static class SemVerConverter
method FromString (line 5) | public static SemVer FromString(string semVerString)
method ToString (line 32) | public static string ToString(SemVer semVer)
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerErrorMessage.cs
class SemVerErrorMessage (line 3) | public static class SemVerErrorMessage
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerValidationResult.cs
class SemVerValidationResult (line 9) | public class SemVerValidationResult
method SemVerValidationResult (line 26) | internal SemVerValidationResult(ReadOnlyCollection<string> errors, Sem...
FILE: Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/SemVerValidator.cs
class SemVerValidator (line 8) | internal class SemVerValidator
method Validate (line 13) | public SemVerValidationResult Validate(SemVer semVer)
method ValidatePreRelease (line 22) | private void ValidatePreRelease(SemVer semVer)
method ValidateBuild (line 30) | private void ValidateBuild(SemVer semVer)
method ValidateIdentifiers (line 38) | private string[] ValidateIdentifiers(string identifiers)
method ValidateLeadingZeroes (line 60) | private string[] ValidateLeadingZeroes(IList<string> identifiers)
method JoinIdentifiers (line 82) | private static string JoinIdentifiers(string[] identifiers)
FILE: Packages/com.unity.ide.rider/Rider/Editor/Discovery.cs
type IDiscovery (line 9) | internal interface IDiscovery
method PathCallback (line 11) | CodeEditor.Installation[] PathCallback();
class Discovery (line 14) | internal class Discovery : IDiscovery
method Discovery (line 19) | static Discovery()
method PathCallback (line 26) | public CodeEditor.Installation[] PathCallback()
class RiderLocatorEnvironment (line 57) | internal class RiderLocatorEnvironment : IRiderLocatorEnvironment
method FromJson (line 77) | public T FromJson<T>(string json)
method Verbose (line 82) | public void Verbose(string message, Exception e = null)
method Info (line 90) | public void Info(string message, Exception e = null)
method Warn (line 97) | public void Warn(string message, Exception e = null)
method Error (line 104) | public void Error(string message, Exception e = null)
FILE: Packages/com.unity.ide.rider/Rider/Editor/EditorPluginInterop.cs
class EditorPluginInterop (line 10) | internal static class EditorPluginInterop
method DisableSyncSolutionOnceCallBack (line 43) | private static void DisableSyncSolutionOnceCallBack()
method OpenFileDllImplementation (line 84) | public static bool OpenFileDllImplementation(string path, int line, in...
method EditorPluginIsLoadedFromAssets (line 115) | public static bool EditorPluginIsLoadedFromAssets(Assembly assembly)
method InitEntryPoint (line 125) | internal static void InitEntryPoint(Assembly assembly)
FILE: Packages/com.unity.ide.rider/Rider/Editor/LoggingLevel.cs
type LoggingLevel (line 3) | internal enum LoggingLevel
FILE: Packages/com.unity.ide.rider/Rider/Editor/PluginSettings.cs
class PluginSettings (line 6) | internal static class PluginSettings
method RiderPreferencesItem (line 26) | [SettingsProvider]
method LinkButton (line 101) | public static bool LinkButton(string url)
FILE: Packages/com.unity.ide.rider/Rider/Editor/PostProcessors/RiderAssetPostprocessor.cs
class RiderAssetPostprocessor (line 6) | internal class RiderAssetPostprocessor: AssetPostprocessor
method OnPreGeneratingCSProjectFiles (line 8) | public static bool OnPreGeneratingCSProjectFiles()
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/AssemblyNameProvider.cs
class AssemblyNameProvider (line 11) | internal class AssemblyNameProvider : IAssemblyNameProvider
method GetAssemblyNameFromScriptPath (line 37) | public string GetAssemblyNameFromScriptPath(string path)
method GetAllAssemblies (line 42) | public Assembly[] GetAllAssemblies()
method GetAssembliesByType (line 65) | private static Assembly[] GetAssembliesByType(AssembliesType type)
method GetNamedAssembly (line 98) | public Assembly GetNamedAssembly(string name)
method GetProjectName (line 109) | public string GetProjectName(string name, string[] defines)
method GetAllAssetPaths (line 116) | public IEnumerable<string> GetAllAssetPaths()
method GetPackageRootDirectoryName (line 121) | private static string GetPackageRootDirectoryName(string assetPath)
method GetPackageInfoForAssetPath (line 134) | public PackageInfo GetPackageInfoForAssetPath(string assetPath)
method ResetCaches (line 158) | public void ResetCaches()
method IsInternalizedPackagePath (line 166) | public bool IsInternalizedPackagePath(string path)
method ParseResponseFile (line 210) | public ResponseFileData ParseResponseFile(string responseFilePath, str...
method GetRoslynAnalyzerPaths (line 229) | public IEnumerable<string> GetRoslynAnalyzerPaths()
method ToggleProjectGeneration (line 236) | public void ToggleProjectGeneration(ProjectGenerationFlag preference)
method ResetProjectGenerationFlag (line 248) | public void ResetProjectGenerationFlag()
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/FileIOProvider.cs
class FileIOProvider (line 8) | class FileIOProvider : IFileIO
method Exists (line 10) | public bool Exists(string path)
method GetReader (line 15) | public TextReader GetReader(string path)
method ReadAllText (line 20) | public string ReadAllText(string path)
method WriteAllText (line 25) | public void WriteAllText(string path, string content)
method EscapedRelativePathFor (line 31) | public string EscapedRelativePathFor(string file, string rootDirectory...
method SkipPathPrefix (line 41) | private static string SkipPathPrefix(string path, string prefix)
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/GUIDProvider.cs
class GUIDProvider (line 2) | class GUIDProvider : IGUIDGenerator
method ProjectGuid (line 4) | public string ProjectGuid(string name)
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IAssemblyNameProvider.cs
type IAssemblyNameProvider (line 8) | internal interface IAssemblyNameProvider
method GetAssemblyNameFromScriptPath (line 14) | string GetAssemblyNameFromScriptPath(string path);
method GetProjectName (line 15) | string GetProjectName(string name, string[] defines);
method IsInternalizedPackagePath (line 16) | bool IsInternalizedPackagePath(string path);
method GetAllAssemblies (line 17) | Assembly[] GetAllAssemblies();
method GetNamedAssembly (line 18) | Assembly GetNamedAssembly(string name);
method GetAllAssetPaths (line 19) | IEnumerable<string> GetAllAssetPaths();
method GetPackageInfoForAssetPath (line 20) | UnityEditor.PackageManager.PackageInfo GetPackageInfoForAssetPath(stri...
method ParseResponseFile (line 21) | ResponseFileData ParseResponseFile(string responseFilePath, string pro...
method GetRoslynAnalyzerPaths (line 22) | IEnumerable<string> GetRoslynAnalyzerPaths();
method ToggleProjectGeneration (line 23) | void ToggleProjectGeneration(ProjectGenerationFlag preference);
method ResetCaches (line 24) | void ResetCaches();
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IFileIO.cs
type IFileIO (line 5) | internal interface IFileIO
method Exists (line 7) | bool Exists(string path);
method GetReader (line 9) | TextReader GetReader(string path);
method ReadAllText (line 10) | string ReadAllText(string path);
method WriteAllText (line 11) | void WriteAllText(string path, string content);
method EscapedRelativePathFor (line 15) | string EscapedRelativePathFor(string path, string rootDirectoryFullPath);
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IGUIDGenerator.cs
type IGUIDGenerator (line 3) | internal interface IGUIDGenerator
method ProjectGuid (line 5) | string ProjectGuid(string name);
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IGenerator.cs
type IGenerator (line 5) | internal interface IGenerator
method SyncIfNeeded (line 7) | bool SyncIfNeeded(IEnumerable<string> affectedFiles, IEnumerable<strin...
method Sync (line 8) | void Sync();
method HasSolutionBeenGenerated (line 9) | bool HasSolutionBeenGenerated();
method SolutionFile (line 10) | string SolutionFile();
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/LastWriteTracker.cs
class LastWriteTracker (line 8) | internal static class LastWriteTracker
method HasLastWriteTimeChanged (line 10) | internal static bool HasLastWriteTimeChanged()
method UpdateLastWriteIfNeeded (line 25) | internal static void UpdateLastWriteIfNeeded(string path)
method IsUnityCompatible (line 41) | internal static bool IsUnityCompatible()
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/PackageManagerTracker.cs
class PackageManagerTracker (line 9) | internal static class PackageManagerTracker
method HasManifestJsonLastWriteTimeChanged (line 11) | private static bool HasManifestJsonLastWriteTimeChanged()
method SyncIfNeeded (line 31) | internal static void SyncIfNeeded(bool checkProjectFiles)
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/ProjectGeneration.cs
class ProjectGeneration (line 14) | internal class ProjectGeneration : IGenerator
type ScriptingLanguage (line 16) | private enum ScriptingLanguage
method ProjectGeneration (line 70) | public ProjectGeneration()
method ProjectGeneration (line 73) | public ProjectGeneration(string projectDirectory)
method ProjectGeneration (line 76) | public ProjectGeneration(string projectDirectory, IAssemblyNameProvide...
method SyncIfNeeded (line 101) | public bool SyncIfNeeded(IEnumerable<string> affectedFiles, IEnumerabl...
method HasFilesBeenModified (line 118) | private bool HasFilesBeenModified(IEnumerable<string> affectedFiles, I...
method ShouldSyncOnReimportedAsset (line 123) | private static bool ShouldSyncOnReimportedAsset(string asset)
method Sync (line 129) | public void Sync()
method HasSolutionBeenGenerated (line 151) | public bool HasSolutionBeenGenerated()
method SetupSupportedExtensions (line 156) | private void SetupSupportedExtensions()
method ShouldFileBePartOfSolution (line 166) | private bool ShouldFileBePartOfSolution(string file)
method HasValidExtension (line 176) | public bool HasValidExtension(string file)
method IsSupportedExtension (line 186) | private bool IsSupportedExtension(string extension)
class AssemblyUsage (line 191) | private class AssemblyUsage
method AddProjectAssembly (line 196) | public void AddProjectAssembly(Assembly assembly)
method AddPrecompiledAssembly (line 201) | public void AddPrecompiledAssembly(Assembly assembly)
method IsProjectAssembly (line 206) | public bool IsProjectAssembly(Assembly assembly) => m_ProjectAssembl...
method IsPrecompiledAssembly (line 207) | public bool IsPrecompiledAssembly(Assembly assembly) => m_Precompile...
method GenerateAndWriteSolutionAndProjects (line 210) | private void GenerateAndWriteSolutionAndProjects(Type[] types)
method AddProjectPart (line 292) | private static ProjectPart AddProjectPart(string assemblyName, Assembl...
method GetAdditionalAssets (line 309) | private Dictionary<string, List<string>> GetAdditionalAssets()
method SyncProject (line 404) | private void SyncProject(StringBuilder stringBuilder, ProjectPart isla...
method SyncProjectFileIfNotChanged (line 412) | private void SyncProjectFileIfNotChanged(string path, string newConten...
method SyncSolutionFileIfNotChanged (line 422) | private void SyncSolutionFileIfNotChanged(string path, string newConte...
method OnGeneratedCSProjectFiles (line 429) | private static void OnGeneratedCSProjectFiles(Type[] types)
method GetAssetPostprocessorTypes (line 447) | public static Type[] GetAssetPostprocessorTypes()
method OnPreGeneratingCSProjectFiles (line 452) | private static bool OnPreGeneratingCSProjectFiles(Type[] types)
method OnGeneratedCSProject (line 476) | private static string OnGeneratedCSProject(string path, string content...
method OnGeneratedSlnSolution (line 499) | private static string OnGeneratedSlnSolution(string path, string conte...
method SyncFileIfNotChanged (line 522) | private void SyncFileIfNotChanged(string path, string newContents)
method HasChanged (line 530) | private bool HasChanged(string path, string newContents)
method ProjectText (line 568) | private string ProjectText(StringBuilder projectBuilder, ProjectPart a...
method ProjectFile (line 639) | private string ProjectFile(ProjectPart projectPart)
method SolutionFile (line 644) | public string SolutionFile()
method ProjectHeader (line 649) | private void ProjectHeader(StringBuilder stringBuilder, ProjectPart as...
method GetGlobalAnalyzerConfigFile (line 754) | private static string GetGlobalAnalyzerConfigFile(ProjectPart assembly)
method GetRoslynAdditionalFiles (line 772) | private static string[] GetRoslynAdditionalFiles(ProjectPart assembly,...
method GetRoslynAnalyzers (line 791) | string[] GetRoslynAnalyzers(ProjectPart assembly, ILookup<string, stri...
method GetRoslynAnalyzerRulesetPaths (line 813) | private IEnumerable<string> GetRoslynAnalyzerRulesetPaths(ProjectPart ...
method AppendWarningAsError (line 824) | private static void AppendWarningAsError(StringBuilder stringBuilder,
method SyncSolution (line 847) | private void SyncSolution(StringBuilder stringBuilder, List<ProjectPar...
method SolutionText (line 852) | private string SolutionText(StringBuilder stringBuilder, List<ProjectP...
method GetOtherArgumentsFromResponseFilesData (line 898) | private static ILookup<string, string> GetOtherArgumentsFromResponseFi...
method GetLangVersion (line 937) | private string GetLangVersion(IEnumerable<string> langVersionList, Pro...
method GetNoWarn (line 949) | public static IEnumerable<string> GetNoWarn(List<string> codes)
method ProjectGuid (line 966) | private string ProjectGuid(string name)
method GetNormalisedAssemblyPath (line 977) | private string GetNormalisedAssemblyPath(string path)
method GetAssemblyNameFromPath (line 989) | private string GetAssemblyNameFromPath(string path)
class FilePathTrie (line 1001) | internal class FilePathTrie<TData>
class TrieNode (line 1007) | private class TrieNode
method Insert (line 1013) | public void Insert(string filePath, TData data)
method FindClosestMatch (line 1032) | public TData FindClosestMatch(string filePath)
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/ProjectGenerationFlag.cs
type ProjectGenerationFlag (line 5) | [Flags]
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/ProjectPart.cs
class ProjectPart (line 8) | internal class ProjectPart
method ProjectPart (line 21) | public ProjectPart(string name, Assembly assembly, List<string> additi...
method GetResponseFileData (line 39) | public List<ResponseFileData> GetResponseFileData(IAssemblyNameProvide...
FILE: Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/SolutionGuidGenerator.cs
class SolutionGuidGenerator (line 7) | internal static class SolutionGuidGenerator
method GuidForProject (line 9) | public static string GuidForProject(string projectName)
method ComputeGuidHashFor (line 14) | private static string ComputeGuidHashFor(string input)
FILE: Packages/com.unity.ide.rider/Rider/Editor/RiderInitializer.cs
class RiderInitializer (line 11) | internal class RiderInitializer
method Initialize (line 13) | public void Initialize(string editorPath)
method LoadEditorPluginForDevEditor (line 60) | private static void LoadEditorPluginForDevEditor(string editorPath)
FILE: Packages/com.unity.ide.rider/Rider/Editor/RiderScriptEditor.cs
class RiderScriptEditor (line 17) | [InitializeOnLoad]
method RiderScriptEditor (line 25) | static RiderScriptEditor()
method ShowWarningOnUnexpectedScriptEditor (line 43) | private static void ShowWarningOnUnexpectedScriptEditor(string path)
method GetEditorRealPath (line 69) | internal static string GetEditorRealPath(string path)
method RiderScriptEditor (line 100) | public RiderScriptEditor(IDiscovery discovery, IGenerator projectGener...
method OnGUI (line 106) | public void OnGUI()
method RegenerateProjectFiles (line 137) | void RegenerateProjectFiles()
method SettingsButton (line 147) | void SettingsButton(ProjectGenerationFlag preference, string guiMessag...
method SyncIfNeeded (line 157) | public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, s...
method SyncAll (line 164) | public void SyncAll()
method SyncSolution (line 169) | [UsedImplicitly]
method SyncIfNeeded (line 175) | [UsedImplicitly] // called from Rider EditorPlugin with reflection
method SyncSolutionAndOpenExternalEditor (line 182) | [UsedImplicitly]
method Initialize (line 194) | public void Initialize(string editorInstallationPath)
method InitializeInternal (line 220) | private static void InitializeInternal(string currentEditorPath)
method OpenProject (line 305) | public bool OpenProject(string path, int line, int column)
method GetSolutionFile (line 326) | private string GetSolutionFile(string path)
method IsUnityScript (line 342) | static bool IsUnityScript(string path)
method GetBaseUnityDeveloperFolder (line 359) | static string GetBaseUnityDeveloperFolder()
method TryGetInstallationForPath (line 364) | public bool TryGetInstallationForPath(string editorPath, out CodeEdito...
method IsRiderOrFleetInstallation (line 401) | public static bool IsRiderOrFleetInstallation(string path)
method ExecutableStartsWith (line 423) | public static bool ExecutableStartsWith(string path, string input)
method IsAssetImportWorkerProcess (line 430) | private static bool IsAssetImportWorkerProcess()
method CreateSolutionIfDoesntExist (line 446) | private void CreateSolutionIfDoesntExist()
FILE: Packages/com.unity.ide.rider/Rider/Editor/RiderScriptEditorData.cs
class RiderScriptEditorData (line 11) | internal class RiderScriptEditorData : ScriptableSingleton<RiderScriptEd...
method Init (line 23) | public void Init()
method InvalidateSavedCompilationDefines (line 31) | public void InvalidateSavedCompilationDefines()
method HasChangesInCompilationDefines (line 36) | public bool HasChangesInCompilationDefines()
method Invalidate (line 44) | public void Invalidate(string editorInstallationPath, bool shouldInval...
FILE: Packages/com.unity.ide.rider/Rider/Editor/RiderScriptEditorDataPersisted.cs
class RiderScriptEditorPersistedState (line 7) | #if UNITY_2020_1_OR_NEWER // API doesn't exist in 2019.4
FILE: Packages/com.unity.ide.rider/Rider/Editor/RiderStyles.cs
class RiderStyles (line 6) | internal static class RiderStyles
method RiderStyles (line 8) | static RiderStyles()
FILE: Packages/com.unity.ide.rider/Rider/Editor/StartUpMethodExecutor.cs
class StartUpMethodExecutor (line 7) | [UsedImplicitly]
method EnterPlayMode (line 10) | [UsedImplicitly]
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/CallbackData.cs
class CallbackData (line 9) | internal class CallbackData : ScriptableSingleton<CallbackData>
method RaiseChangedEvent (line 19) | internal void RaiseChangedEvent()
method Clear (line 27) | [UsedImplicitly] // Is used by Rider Unity plugin by reflection
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/CallbackInitializer.cs
class CallbackInitializer (line 8) | [InitializeOnLoad]
method CallbackInitializer (line 11) | static CallbackInitializer()
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/RiderTestRunner.cs
class RiderTestRunner (line 18) | [UsedImplicitly]
method RunTestsWithSyncCallbacks (line 39) | [UsedImplicitly]
method RunTests (line 62) | [UsedImplicitly]
method CancelTestRun (line 115) | [UsedImplicitly]
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/SyncTestRunCallback.cs
class SyncTestRunCallback (line 10) | internal class SyncTestRunCallback : ITestRunCallback
method RunStarted (line 12) | public void RunStarted(ITest testsToRun)
method RunFinished (line 16) | public void RunFinished(ITestResult testResults)
method TestStarted (line 21) | public void TestStarted(ITest test)
method TestFinished (line 27) | public void TestFinished(ITestResult result)
method GenerateId (line 34) | private static string GenerateId(ITest node)
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/SyncTestRunEventsHandler.cs
class SyncTestRunEventsHandler (line 9) | internal class SyncTestRunEventsHandler : ScriptableSingleton<SyncTestRu...
method InitRun (line 23) | internal void InitRun(string sessionId, string handlerCodeBase, string...
method OnEnable (line 42) | private void OnEnable()
method OnTestStarted (line 48) | internal void OnTestStarted(string testId)
method OnTestFinished (line 54) | internal void OnTestFinished()
method OnRunFinished (line 60) | internal void OnRunFinished()
method SafeInvokeHandlerMethod (line 70) | private void SafeInvokeHandlerMethod(MethodInfo methodInfo, object[] a...
method CreateHandlerInstance (line 82) | private void CreateHandlerInstance()
method CleanUp (line 138) | private void CleanUp()
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/TestEvent.cs
type EventType (line 10) | [Serializable]
class TestEvent (line 16) | [Serializable]
method TestEvent (line 27) | public TestEvent(EventType type, string id, string assemblyName, strin...
FILE: Packages/com.unity.ide.rider/Rider/Editor/UnitTesting/TestsCallback.cs
class TestsCallback (line 10) | internal class TestsCallback : ScriptableObject, IErrorCallbacks
method RunFinished (line 12) | public void RunFinished(ITestResultAdaptor result)
method RunStarted (line 21) | public void RunStarted(ITestAdaptor testsToRun)
method TestStarted (line 28) | public void TestStarted(ITestAdaptor result)
method TestFinished (line 39) | public void TestFinished(ITestResultAdaptor result)
method OnError (line 49) | public void OnError(string message)
method GenerateId (line 59) | private static string GenerateId(ITestAdaptor node)
method ParseTestStatus (line 71) | private static NUnit.Framework.Interfaces.TestStatus ParseTestStatus(T...
method ExtractOutput (line 76) | private static string ExtractOutput(ITestResultAdaptor testResult)
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/CommandLineParser.cs
class CommandLineParser (line 5) | internal class CommandLineParser
method CommandLineParser (line 9) | public CommandLineParser(string[] args)
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/FileSystemUtil.cs
class FileSystemUtil (line 10) | internal static class FileSystemUtil
method GetFinalPathName (line 12) | [NotNull]
method FileNameWithoutExtension (line 29) | public static string FileNameWithoutExtension(string path)
method EditorPathExists (line 60) | public static bool EditorPathExists(string editorPath)
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/LibcNativeInterop.cs
class LibcNativeInterop (line 7) | internal static class LibcNativeInterop
method realpath (line 9) | [DllImport("libc", SetLastError = true)]
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/RiderMenu.cs
class RiderMenu (line 11) | [UsedImplicitly]
method MenuOpenProject (line 17) | [UsedImplicitly]
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/RiderPathUtil.cs
class RiderPathUtil (line 5) | internal static class RiderPathUtil
method IsRiderDevEditor (line 7) | public static bool IsRiderDevEditor(string editorPath)
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/SerializableVersion.cs
class SerializableVersion (line 5) | [Serializable]
method SerializableVersion (line 8) | public SerializableVersion(Version version)
class VersionExtension (line 25) | internal static class VersionExtension
method ToSerializableVersion (line 27) | public static SerializableVersion ToSerializableVersion(this Version v...
method ToVersion (line 35) | public static Version ToVersion(this SerializableVersion serializableV...
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/StringBuilderExtensions.cs
class StringBuilderExtensions (line 6) | internal static class StringBuilderExtensions
method CompatibleAppendJoin (line 10) | public static StringBuilder CompatibleAppendJoin(this StringBuilder st...
FILE: Packages/com.unity.ide.rider/Rider/Editor/Util/StringUtils.cs
class StringUtils (line 5) | internal static class StringUtils
method NormalizePath (line 7) | public static string NormalizePath(this string path)
Condensed preview — 304 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (994K chars).
[
{
"path": ".gitattributes",
"chars": 2518,
"preview": "###############################################################################\n# Set default behavior to automatically "
},
{
"path": ".gitignore",
"chars": 642,
"preview": "#IDE文件\n.vs/\n.idea/\n.vscode/\n_ReSharper.CSharp/\n\n#svn\n*/.svn/\n.svn/\n\n#Excel的临时文件\n~$*.xlsx\n~$*.xlsx.meta\n\n#Unity相关目录\n.grad"
},
{
"path": "Assets/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "Assets/DefaultVolumeProfile.asset",
"chars": 18737,
"preview": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &-8794448539801599142\nMonoBehaviour:\n m_ObjectHideFlags: 3\n m_Corr"
},
{
"path": "Assets/DefaultVolumeProfile.asset.meta",
"chars": 189,
"preview": "fileFormatVersion: 2\nguid: a722862efbc47421c908e1e722eba667\nNativeFormatImporter:\n externalObjects: {}\n mainObjectFile"
},
{
"path": "Assets/Resources/BuildinFileManifest.asset",
"chars": 2404,
"preview": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &11400000\nMonoBehaviour:\n m_ObjectHideFlags: 0\n m_CorrespondingSou"
},
{
"path": "Assets/Resources/BuildinFileManifest.asset.meta",
"chars": 189,
"preview": "fileFormatVersion: 2\nguid: e70bfb3ccac3b4f43af72c0a8b8cdd63\nNativeFormatImporter:\n externalObjects: {}\n mainObjectFile"
},
{
"path": "Assets/Resources.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: fe14f6ff4d78a464fbc6dd761d73e38e\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Assets/Settings/Build Profiles/Mac.asset",
"chars": 44635,
"preview": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &11400000\nMonoBehaviour:\n m_ObjectHideFlags: 0\n m_CorrespondingSou"
},
{
"path": "Assets/Settings/Build Profiles/Mac.asset.meta",
"chars": 189,
"preview": "fileFormatVersion: 2\nguid: b6ae1720552ce4174bde3c7ee1ff65aa\nNativeFormatImporter:\n externalObjects: {}\n mainObjectFile"
},
{
"path": "Assets/Settings/Build Profiles.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: 1c7616f0661a042bab7d5e2ed3c6aac1\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Assets/Settings.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: a9ccb235ecf71412cb659c2bd837eba0\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Assets/link.xml",
"chars": 694,
"preview": "<linker>\n <assembly fullname=\"ET.Core\" preserve=\"all\"/>\n <assembly fullname=\"ET.Loader\" preserve=\"all\"/>\n <asse"
},
{
"path": "Assets/link.xml.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 4c3a563f505464efb8376db1bae84009\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Book/1.1运行指南.md",
"chars": 4238,
"preview": "# 运行步骤\n1. IDE安装\n\n **使用[Rider2024.3](https://www.jetbrains.com/zh-cn/rider/)**(更新到最新版),需要安装以下内容:\n - windows上用visual s"
},
{
"path": "Book/1.2Why use .net core.md",
"chars": 5020,
"preview": "# Why use C# .net core for server-side?\nGame server side from the early single service to distributed, the development i"
},
{
"path": "Book/1.2为什么使用.net core.md",
"chars": 1878,
"preview": "# 为什么使用C# .net core做服务端?\n游戏服务端从早期的单服到分布式,开发越来越复杂,对稳定性,开发效率要求越来越高。开发语言的选择也逐步发生了变化,C 到 C++ 到 C++ + PYTHON 或者C++ + LUA 到现在 "
},
{
"path": "Book/2.1CSharp Coroutine.md",
"chars": 6162,
"preview": "# What is a concurrent thread\nSpeaking of concurrency, let's first understand what is asynchronous. Asynchronous simply "
},
{
"path": "Book/2.1CSharp的协程.md",
"chars": 4099,
"preview": "# 什么是协程\n说到协程,我们先了解什么是异步,异步简单说来就是,我要发起一个调用,但是这个被调用方(可能是其它线程,也可能是IO)出结果需要一段时间,我不想让这个调用阻塞住调用方的整个线程,因此传给被调用方一个回调函数,被调用方运行完成后"
},
{
"path": "Book/2.2Better Coroutine.md",
"chars": 4307,
"preview": "# Better Coroutine\nThe above article talks about how a string of callbacks is a concurrent process, and obviously writin"
},
{
"path": "Book/2.2更好的协程.md",
"chars": 2953,
"preview": "# 更好的协程\n上文讲了一串回调就是协程,显然这样写代码,增加逻辑,插入逻辑非常容易出错。我们需要利用异步语法把这个异步回调的形式改成同步的形式,幸好C#已经帮我们设计好了,看代码\n```csharp\n // example2_2\n "
},
{
"path": "Book/2.3Single-threaded asynchronous.md",
"chars": 5239,
"preview": "# Single-threaded asynchronous\nThe previous examples are multi-threaded implementations of asynchrony, but asynchrony is"
},
{
"path": "Book/2.3单线程异步.md",
"chars": 4327,
"preview": "# 单线程异步\n前面几个例子都是多线程实现的异步,但是异步显然不仅仅是多线程的。我们在之前的例子中使用了Sleep来实现时间的等待,每一个计时器都需要使用一个线程,会导致线程切换频繁,这个实现效率很低,平常是不会这样做的。一般游戏逻辑中会设"
},
{
"path": "Book/3.2The powerful MongoBson library.md",
"chars": 9701,
"preview": "# The powerful MongoBson library\nBack-end development, statistics about these scenarios need to use serialization: 1.\n1."
},
{
"path": "Book/3.2强大的MongoBson库.md",
"chars": 6201,
"preview": "# 强大的MongoBson库\n后端开发,统计了一下大概有这些场景需要用到序列化:\n1. 对象通过序列化反序列化clone\n2. 服务端数据库存储数据,二进制\n3. 分布式服务端,多进程间的消息,二进制\n4. 后端日志,文本格式\n5. 服务"
},
{
"path": "Book/3.3Everything is Entity.md",
"chars": 6581,
"preview": "# Everything is Entity\nThe ECS design is very popular right now, mainly because of the success of Watchtower, which has "
},
{
"path": "Book/3.3一切皆实体.md",
"chars": 3296,
"preview": "# 一切皆实体\n目前十分流行ECS设计,主要是守望先锋的成功,引爆了这种技术。守望先锋采用了状态帧这种网络技术,客户端会进行预测,预测不准需要进行回滚,由于组件式的设计,回滚可以只回滚某些组件即可。ECS最重要的设计是逻辑跟数据的完全分离。"
},
{
"path": "Book/3.4EventSystem.md",
"chars": 5111,
"preview": "# EventSystem \nOne of the most important features of ECS is the separation of data and logic, and the second is data-dr"
},
{
"path": "Book/3.4事件机制EventSystem.md",
"chars": 3320,
"preview": "# 事件机制EventSystem \nECS最重要的特性一是数据跟逻辑分离,二是数据驱动逻辑。什么是数据驱动逻辑呢?不太好理解,我们举个例子\n一个moba游戏,英雄都有血条,血条会在人物头上显示,也会在左上方头像UI上显示。这时候服务端发"
},
{
"path": "Book/4.1Component-based design.md",
"chars": 10670,
"preview": "# Component-based design\n\nIn terms of code reuse and organization of data, object-oriented may be the first response. Ob"
},
{
"path": "Book/4.1组件式设计.md",
"chars": 4048,
"preview": "# 组件式设计\n\n在代码复用和组织数据方面,面向对象可能是大家第一反应。面向对象三大特性继承,封装,多态,在一定程度上能解决不少代码复用,数据复用的问题。不过面向对象不是万能的,它也有极大的缺陷: \n\n## 1. 数据结构耦合性极强\n一旦"
},
{
"path": "Book/5.4Actor Model.md",
"chars": 9655,
"preview": "# Actor model\n### Actor introduction\nBefore discussing the Actor model, we should discuss the architecture of ET. There "
},
{
"path": "Book/5.4Actor模型.md",
"chars": 4795,
"preview": "# Actor模型\n### Actor介绍\n在讨论Actor模型之前先要讨论下ET的架构,游戏服务器为了利用多核一般有两种架构,单线程多进程跟单进程多线程架构。两种架构本质上其实区别不大,因为游戏逻辑开发都需要用单线程,即使是单进程多线程架"
},
{
"path": "Book/5.5Actor Location-EN.md",
"chars": 10643,
"preview": "# Actor Location\n### Actor Location\nActor model only needs to know each other's InstanceId to send messages, which is ve"
},
{
"path": "Book/5.5Actor Location-ZH.md",
"chars": 4686,
"preview": "# Actor Location\n### Actor Location\nActor模型只需要知道对方的InstanceId就能发送消息,十分方便,但是有时候我们可能无法知道对方的InstanceId,或者是一个Actor的InstanceI"
},
{
"path": "Book/5.6Numerical component design.md",
"chars": 8186,
"preview": "Similar to world of warcraft, moba such skills are extremely complex, flexibility requires a very high skill system, mus"
},
{
"path": "Book/5.6数值组件设计.md",
"chars": 5028,
"preview": "类似魔兽世界,moba这种技能极其复杂,灵活性要求极高的技能系统,必须需要一套及其灵活的数值结构来搭配。数值结构设计好了,实现技能系统就会非常简单,否则就是一场灾难。比如魔兽世界,一个人物的数值属性非常之多,移动速度,力量,怒气,能量,集中"
},
{
"path": "Book/6.1AI Framwork.md",
"chars": 8537,
"preview": "# AI framework\n## 1. Several AI designs\nAI in the game a lot, but why do people always feel ai writing up very difficult"
},
{
"path": "Book/6.1AI框架.md",
"chars": 3468,
"preview": "# AI框架\n## 1. 几种AI的设计\nAI在游戏中很多,但是为什么大家总是感觉ai编写起来十分困难,我后来思考了一番,主要原因是使用的方法不当。之前大家编写ai主要有几种方案:\n### a. 状态机 \n我是不知道谁想出来这个做法的,真"
},
{
"path": "Book/6.2AI框架-行为机.md",
"chars": 3587,
"preview": "# AI框架介绍-行为机(Behavior Machine)\n## 什么是行为机\n顾名思义,类比状态机每个节点是一个状态,行为机每个节点是描述一种行为。行为机每个节点之间是互斥的,并且节点相互之间完全不用关心是怎么切换的。这里就不讲状态机跟"
},
{
"path": "Book/7.1代码规范.md",
"chars": 59,
"preview": "# 代码规范\n[分析器说明](https://www.yuque.com/u28961999/yms0nt/) \n\n"
},
{
"path": "Book/8.1ET Package制作指南.md",
"chars": 2752,
"preview": "# ET Package规范\n1. ET Package是标准的Unity Package\n \n2. ET Package包目录的格式是cn.etetet.+包名,例如cn.etetet.core\n \n3. ET Package 本"
},
{
"path": "Book/8.2ET Package目录.md",
"chars": 12843,
"preview": "ET Package目录,简单描述,具体说明请看包中的Readme\n\n| 编号 | 包名 "
},
{
"path": "Book/8.3ET9项目怎么进行包更新.md",
"chars": 156,
"preview": "ET9变成了包的形式,但是对用用户来说,区别不大,大家安装好包之后,将项目跟包全部传到自己的git仓库作为master分支,\n开发的时候切换出来一个分支dev进行开发。每次要更新,则先切换到master分支进行更新,更新完成后提交到mast"
},
{
"path": "ChangeLog.md",
"chars": 455,
"preview": "|包名| 版本 | 更新内容 |\n|-------|-------|-------|\n|cn.etetet.core| 2.0.6 | 修复一个SoretedDictionary跟SortedSet只读遍历的时候多线程不安全的问题 |\n|"
},
{
"path": "Directory.Build.props",
"chars": 226,
"preview": "<Project>\n <PropertyGroup>\n <LangVersion>11.0</LangVersion>\n <NoWarn>0169,0649,3021,8981</NoWarn>\n <DebugType>"
},
{
"path": "ET.sln.DotSettings",
"chars": 123007,
"preview": "<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namesp"
},
{
"path": "LICENSE",
"chars": 388,
"preview": "ET License\n\n1. 该ET版本版权属于广州市一踢互联网科技有限公司所有。 \n2. 该ET版本可以任意修改代码,或者在ET基础上添加代码,但是仅限于自己或者自己任职的公司使用,不得传播给他人,私下传播ET修改版或者基于ET开发的代"
},
{
"path": "Packages/.gitignore",
"chars": 11,
"preview": "cn.etetet.*"
},
{
"path": "Packages/com.etetet.init/.gitignore",
"chars": 67,
"preview": "obj/\n/**/AssemblyReference.asmref\n/**/AssemblyReference.asmref.meta"
},
{
"path": "Packages/com.etetet.init/Editor/ET.Init.Editor.asmdef",
"chars": 392,
"preview": "{\n \"name\": \"ET.Init.Editor\",\n \"rootNamespace\": \"\",\n \"references\": [\n \"ET.Init\"\n ],\n \"includePlatfo"
},
{
"path": "Packages/com.etetet.init/Editor/ET.Init.Editor.asmdef.meta",
"chars": 166,
"preview": "fileFormatVersion: 2\nguid: 255f768bf23bb4542a2a2f6defebc2e3\nAssemblyDefinitionImporter:\n externalObjects: {}\n userData"
},
{
"path": "Packages/com.etetet.init/Editor/GitDependencyResolver/DependencyResolver.cs",
"chars": 3772,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing ET;\n"
},
{
"path": "Packages/com.etetet.init/Editor/GitDependencyResolver/DependencyResolver.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: d087a58f7e93a0b449fdce47cfbd701b\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.etetet.init/Editor/GitDependencyResolver.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: ce7769db96264d48961e70d63f9d52ab\ntimeCreated: 1717601777"
},
{
"path": "Packages/com.etetet.init/Editor/PackageGit.cs",
"chars": 940,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing MongoDB.Bson.Serialization;\nusing MongoDB.Bson.S"
},
{
"path": "Packages/com.etetet.init/Editor/PackageGit.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: b809ee54965a437f991cddc40f76e4da\ntimeCreated: 1720094387"
},
{
"path": "Packages/com.etetet.init/Editor/ProcessHelper.cs",
"chars": 2344,
"preview": "using System;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing System.Threading.Tasks;\nusing Debug"
},
{
"path": "Packages/com.etetet.init/Editor/ProcessHelper.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: a193dc186ced416791dd3aa22dc2c2c7\ntimeCreated: 1720432378"
},
{
"path": "Packages/com.etetet.init/Editor.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: bbb18281570c7404da25cd964799c89d\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.etetet.init/MoveToPackages.ps1",
"chars": 251,
"preview": "param($packageName, $version)\n\nWrite-Host $packageName, $version\n\n$from = \"Library/PackageCache/$packageName@\" + $versio"
},
{
"path": "Packages/com.etetet.init/MoveToPackages.ps1.meta",
"chars": 155,
"preview": "fileFormatVersion: 2\nguid: 202ab9d4b020c1f4bad8937d2497fba6\nDefaultImporter:\n externalObjects: {}\n userData: \n assetB"
},
{
"path": "Packages/com.etetet.init/MoveToPackages_6.ps1",
"chars": 222,
"preview": "param($packageName, $version)\n\nWrite-Host $packageName\n\n$from = \"Library/PackageCache/$packageName\" \nMove-Item $from \"Pa"
},
{
"path": "Packages/com.etetet.init/MoveToPackages_6.ps1.meta",
"chars": 155,
"preview": "fileFormatVersion: 2\nguid: 6997902c080851b4586429ad0eb5197b\nDefaultImporter:\n externalObjects: {}\n userData: \n assetB"
},
{
"path": "Packages/com.etetet.init/Plugins/MongoDB.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: d7e6591a3956b6742880c17ccb9ee588\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.etetet.init/Plugins.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: cfc13919d8b071d458f99014dca6462c\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.etetet.init/package.json",
"chars": 329,
"preview": "{\n \"name\": \"com.etetet.init\",\n \"displayName\": \"ET.Init\",\n \"version\": \"0.0.1\",\n \"unity\": \"2022.3\",\n \"description\": \""
},
{
"path": "Packages/com.etetet.init/package.json.meta",
"chars": 163,
"preview": "fileFormatVersion: 2\nguid: cc4817c723bbe5542aab7d1ee4e11cc9\nPackageManifestImporter:\n externalObjects: {}\n userData: \n"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/CHANGELOG.md",
"chars": 53,
"preview": "# ChangeLog\n\n\n## 0.0.1 - 2020-05-11\n- Package created"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/CHANGELOG.md.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 3e6d9b2004fec8de1adc49c18ba1bdcb\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/CredentialManager.cs",
"chars": 4176,
"preview": "\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing Tomlyn;\nusing Tomlyn.Model;\nu"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/CredentialManager.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: d007b6fca56974d8f856529be2d2130a\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/RegistryManager.cs",
"chars": 5037,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing Newtonsoft.Json.Linq;\nusing UnityEditor;\nusing U"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/RegistryManager.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: ed727f03f93e90392bda8e4663336ae2\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/ScopedRegistry.cs",
"chars": 1503,
"preview": "using System;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Halodi.PackageRegistry.Core\n{\n [System."
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/ScopedRegistry.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: be826e7a3923f2b79b58b8a5d923ba54\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/UpgradePackagesManager.cs",
"chars": 7536,
"preview": "using UnityEngine;\nusing UnityEditor;\nusing UnityEditor.PackageManager.Requests;\nusing UnityEditor.PackageManager;\nusing"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core/UpgradePackagesManager.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 62211760241e94052a18720a8ae0a8ff\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/Core.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: 4cf3f1318b46e144cbe43e9daf69c5b5\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMLogin.cs",
"chars": 2783,
"preview": "\nusing System;\nusing System.IO;\nusing System.Net;\nusing System.Text;\nusing UnityEngine;\nusing Random = System.Random;\n\nn"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMLogin.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: b48148742aea9c373b5a101a0794ff57\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMPublish.cs",
"chars": 2774,
"preview": "using System;\nusing System.IO;\nusing System.Net;\nusing System.Text;\nusing Halodi.PackageRegistry.Core;\nusing UnityEngine"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMPublish.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 4c14ed213814c5c26835ad084ee31ef0\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMResponse.cs",
"chars": 252,
"preview": "namespace Halodi.PackageRegistry.NPM\n{\n [System.Serializable]\n public class NPMResponse\n {\n public strin"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/NPMResponse.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: e1686b71fd1b8d8ad91a35d5a7994008\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/PackageTarball.cs",
"chars": 3085,
"preview": "using Newtonsoft.Json.Linq;\nusing System.IO;\nusing Unity.SharpZipLib.GZip;\nusing Unity.SharpZipLib.Tar;\n\nnamespace Halod"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/PackageTarball.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 65eda3cbab9699e01aee646d705bf241\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/PublicationManifest.cs",
"chars": 6163,
"preview": "using System;\nusing System.IO;\nusing System.Security.Cryptography;\nusing System.Threading;\nusing Newtonsoft.Json.Linq;\nu"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/PublicationManifest.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 0c6809e15491b0062ad2e9c9aa05a72a\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/WebExceptionParser.cs",
"chars": 1162,
"preview": "using System.Net;\n\nnamespace Halodi.PackageRegistry.NPM\n{\n public class WebExceptionParser\n {\n public stati"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM/WebExceptionParser.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 38840c461d1475a36bb61118358e2b51\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/NPM.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: 2bdc72b5107d76fc9b0eefd914b165d1\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/BulkAddPackages.cs",
"chars": 3262,
"preview": "using System;\nusing System.IO;\nusing System.Threading;\nusing UnityEditor;\nusing UnityEditor.PackageManager;\nusing Unity"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/BulkAddPackages.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 392cfa6b2194898f5a5328604b1e4814\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/CredentialEditorView.cs",
"chars": 4955,
"preview": "\nusing System;\nusing Halodi.PackageRegistry.Core;\nusing UnityEditor;\nusing UnityEngine;\n\nnamespace Halodi.PackageRegistr"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/CredentialEditorView.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: aed147722dfd212f18ab415b2ced4be3\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/CredentialManagerView.cs",
"chars": 2476,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Halodi.PackageRegistry.Core;\nusing Unity"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/CredentialManagerView.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 6f1896f0b6cdab862b835b3498da53bd\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/GetTokenView.cs",
"chars": 4626,
"preview": "using System;\nusing Halodi.PackageRegistry.Core;\nusing Halodi.PackageRegistry.NPM;\nusing UnityEditor;\nusing UnityEngine;"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/GetTokenView.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: c114ccf58900f5e5ab2f67306c0f40c2\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/InstallPackageCreator.cs",
"chars": 482,
"preview": "using System;\nusing System.Collections.Generic;\nusing Halodi.PackageRegistry.Core;\nusing UnityEditor;\nusing UnityEngine;"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/InstallPackageCreator.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 7c16af11ea1aab931a6cc447c7b7bd7d\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/RegistryManagerView.cs",
"chars": 3326,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Halodi.PackageRegistry.Core;\nusing Unit"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/RegistryManagerView.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 0bb92217c830c75c4baccac1d1455d6a\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/ScopedRegistryEditorView.cs",
"chars": 5535,
"preview": "\nusing System;\nusing Halodi.PackageRegistry.Core;\nusing UnityEditor;\nusing UnityEditorInternal;\nusing UnityEngine;\n\nname"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/ScopedRegistryEditorView.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 0fa2f00a8426714b1a94a89df408ce1c\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/SettingsProvider.cs",
"chars": 3869,
"preview": "using System;\nusing System.Collections.Generic;\nusing Halodi.PackageRegistry.Core;\nusing UnityEditor;\nusing UnityEditor."
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/SettingsProvider.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: ef531c9865ba9df44a7b77c821d7bf81\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/UpgradePackagesView.cs",
"chars": 6143,
"preview": "using System;\nusing System.Collections.Generic;\nusing Halodi.PackageRegistry.Core;\nusing UnityEditor;\nusing UnityEngine;"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI/UpgradePackagesView.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 1f38c8e7a61df01009d0a1d9a8cd169e\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry/UI.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: a67360ebdc1e6d247b183859bd4be8a1\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi/PackageRegistry.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: e0dc91072aad56afa8bf4204ca72e442\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi.PackageRegistryManager.Editor.asmdef",
"chars": 522,
"preview": "{\n \"name\": \"Halodi.PackageRegistryManager.Editor\",\n \"rootNamespace\": \"\",\n \"references\": [\n \"Artees.Seman"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi.PackageRegistryManager.Editor.asmdef.meta",
"chars": 166,
"preview": "fileFormatVersion: 2\nguid: 69fe18a9ea685ee439ed87927dfee5c7\nAssemblyDefinitionImporter:\n externalObjects: {}\n userData"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/Halodi.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: 089bef75d43e1ee5888d3be1e71e7ac4\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/LICENSE.Tomlyn.md",
"chars": 1303,
"preview": "Copyright (c) 2019, Alexandre Mutel\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or wit"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/LICENSE.Tomlyn.md.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 475ab608e6dbf99409204c1b770d6562\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/README.md",
"chars": 589,
"preview": "# Third party libraries\n\nTo avoid conflicts between packages all third party managed DLL's are prefixed with \"860BB79D\","
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/README.md.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 0006809df3aab01ef9e2bb535d02ebd9\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Tomlyn.dll.meta",
"chars": 645,
"preview": "fileFormatVersion: 2\nguid: fe280739c6633ac4682879c184af804f\nPluginImporter:\n externalObjects: {}\n serializedVersion: 2"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/Artees.UnitySemVer.asmdef",
"chars": 332,
"preview": "{\n \"name\": \"Artees.SemanticVersioning\",\n \"references\": [],\n \"includePlatforms\": [],\n \"excludePlatforms\": [],"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/Artees.UnitySemVer.asmdef.meta",
"chars": 166,
"preview": "fileFormatVersion: 2\nguid: 807287499d1ce5fd6a509eb37593f7da\nAssemblyDefinitionImporter:\n externalObjects: {}\n userData"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/CloudBuildManifest.cs",
"chars": 1520,
"preview": "using System;\nusing UnityEngine;\n\nnamespace Artees.UnitySemVer\n{\n /// <summary>\n /// A parsed <a href=\"https://doc"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/CloudBuildManifest.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 3827c13e6b66fbeaa8fa6d86711b6ab2\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2019 Artees\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/LICENSE.meta",
"chars": 155,
"preview": "fileFormatVersion: 2\nguid: 3a5b2e83aa2757a0e8adc6834aff5a52\nDefaultImporter:\n externalObjects: {}\n userData: \n assetB"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Editor/ThirdParty/Unity-SemVer/README.md",
"chars": 1188,
"preview": "# Unity SemVer\n[]"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Tests/Editor/AssemblyInfo.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 6e0e426895d792d2ca7bc29a9b7e67f2\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Tests/Editor/Halodi.PackageRegistryManager.Tests.asmdef",
"chars": 555,
"preview": "{\n \"name\": \"Halodi.PackageRegistryManager.Tests\",\n \"references\": [\n \"UnityEngine.TestRunner\",\n \"Unit"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Tests/Editor/Halodi.PackageRegistryManager.Tests.asmdef.meta",
"chars": 166,
"preview": "fileFormatVersion: 2\nguid: f56fe7affb0989254a398cff4738a865\nAssemblyDefinitionImporter:\n externalObjects: {}\n userData"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Tests/Editor.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: d26e9d0d897f2ff3cb41c5d4d4c8985e\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/Tests.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: cb4cfc2933fda19ed95027bf0df94ac1\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/package.json",
"chars": 676,
"preview": "{\n \"name\": \"com.halodi.halodi-unity-package-registry-manager\",\n \"version\": \"1.0.5\",\n \"displayName\": \"Halodi Package R"
},
{
"path": "Packages/com.halodi.halodi-unity-package-registry-manager/package.json.meta",
"chars": 163,
"preview": "fileFormatVersion: 2\nguid: 4480134b80472dc1a847e756ab3917cc\nPackageManifestImporter:\n externalObjects: {}\n userData: \n"
},
{
"path": "Packages/com.unity.ide.rider/.editorconfig",
"chars": 70,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf"
},
{
"path": "Packages/com.unity.ide.rider/.signature",
"chars": 1413,
"preview": "{\n \"timestamp\": 1718627208,\n \"signature\": \"XO721MNQKyDN0H6D282GzXcEQDBd9wgGlkoocQk3iz22Tjp0E9zHrXLlGRpHJv5ltn5k0Dmegdp"
},
{
"path": "Packages/com.unity.ide.rider/CHANGELOG.md",
"chars": 11271,
"preview": "# Code Editor Package for Rider\n\n\n## [3.0.31] - 2024-06-17\n\nfix RIDER-104519 Rider is reporting errors in scripts that w"
},
{
"path": "Packages/com.unity.ide.rider/CHANGELOG.md.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 8645aa9c3c74fb34ba9499e14fb332b5\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Packages/com.unity.ide.rider/CONTRIBUTING.md",
"chars": 477,
"preview": "# Contributing\n\n## All contributions are subject to the [Unity Contribution Agreement(UCA)](https://unity3d.com/legal/li"
},
{
"path": "Packages/com.unity.ide.rider/CONTRIBUTING.md.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 5e83f8baac96eaa47bdd9ca781cd2002\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Packages/com.unity.ide.rider/Documentation~/README.md",
"chars": 156,
"preview": "# Code Editor Package for Rider\n\nThis package is not intended to be modified by users.\nNor does it provide any api inten"
},
{
"path": "Packages/com.unity.ide.rider/Documentation~/TableOfContents.md",
"chars": 132,
"preview": "* [About JetBrains Rider Editor](index.md)\n* [Using the JetBrains Rider Editor package](using-the-jetbrains-rider-editor"
},
{
"path": "Packages/com.unity.ide.rider/Documentation~/index.md",
"chars": 2161,
"preview": "# About JetBrains Rider Editor\n\nThe JetBrains Rider editor package integrates support for the [JetBrains Rider](https://"
},
{
"path": "Packages/com.unity.ide.rider/Documentation~/using-the-jetbrains-rider-editor-package.md",
"chars": 6620,
"preview": "\n# Using the JetBrains Rider Editor package\n\nTo use the package, go to **Edit > Preferences > External Tools**, cl"
},
{
"path": "Packages/com.unity.ide.rider/LICENSE.md",
"chars": 1132,
"preview": "MIT License\n\nCopyright (c) 2019 Unity Technologies Copyright (c) 2019 JetBrains s.r.o. All rights reserved.\n\nPermission "
},
{
"path": "Packages/com.unity.ide.rider/LICENSE.md.meta",
"chars": 158,
"preview": "fileFormatVersion: 2\nguid: 5598b14661b5f4c43bed757f34b6d172\nTextScriptImporter:\n externalObjects: {}\n userData: \n ass"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/Discovery.cs",
"chars": 2939,
"preview": "using System;\nusing System.Linq;\nusing JetBrains.Rider.PathLocator;\nusing Packages.Rider.Editor.Util;\nusing Unity.CodeEd"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/Discovery.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: dab656c79e1985c40b31faebcda44442\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/EditorPluginInterop.cs",
"chars": 5251,
"preview": "using System;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/EditorPluginInterop.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: f9bd02a3a916be64c9b47b1305149423\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/JetBrains.Rider.PathLocator.dll.meta",
"chars": 1286,
"preview": "fileFormatVersion: 2\nguid: ea3ec41b33345cd4f9298a51abdaa198\nPluginImporter:\n externalObjects: {}\n serializedVersion: 2"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/LoggingLevel.cs",
"chars": 664,
"preview": "namespace Packages.Rider.Editor\n{\n internal enum LoggingLevel\n {\n /// <summary>\n /// Do not use it in logging. O"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/LoggingLevel.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 71bb46b59a9a7a346bbab1e185c723df\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/PluginSettings.cs",
"chars": 3856,
"preview": "using UnityEditor;\nusing UnityEngine;\n\nnamespace Packages.Rider.Editor\n{\n internal static class PluginSettings\n {\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/PluginSettings.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 1bfe12aa306c0c74db4f4f1a1a0ae5ce\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/PostProcessors/RiderAssetPostprocessor.cs",
"chars": 482,
"preview": "using Unity.CodeEditor;\nusing UnityEditor;\n\nnamespace Packages.Rider.Editor.PostProcessors\n{\n internal class RiderAsset"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/PostProcessors/RiderAssetPostprocessor.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 45471ad7b8c1f964da5e3c07d57fbf4f\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/PostProcessors.meta",
"chars": 172,
"preview": "fileFormatVersion: 2\nguid: aa290bd9a165a0543a4bf85ac73914bc\nfolderAsset: yes\nDefaultImporter:\n externalObjects: {}\n us"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/AssemblyNameProvider.cs",
"chars": 9104,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEditor;\nusing UnityEditor.Compilation;\nusi"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/AssemblyNameProvider.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: 56c8c6e437b14f8e9a91e9832c11bc1a\ntimeCreated: 1580717719"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/FileIOProvider.cs",
"chars": 1391,
"preview": "using System;\nusing System.IO;\nusing System.Security;\nusing System.Text;\nusing Packages.Rider.Editor.Util;\n\nnamespace Pa"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/FileIOProvider.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: a6ba838b1348d5e46a7eaacd1646c1d3\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/GUIDProvider.cs",
"chars": 212,
"preview": "namespace Packages.Rider.Editor.ProjectGeneration {\n class GUIDProvider : IGUIDGenerator\n {\n public string ProjectG"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/GUIDProvider.cs.meta",
"chars": 243,
"preview": "fileFormatVersion: 2\nguid: 8cfde1a59fb35574189691a9de1df93b\nMonoImporter:\n externalObjects: {}\n serializedVersion: 2\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IAssemblyNameProvider.cs",
"chars": 1017,
"preview": "using System;\nusing System.Collections.Generic;\nusing UnityEditor;\nusing UnityEditor.Compilation;\n\nnamespace Packages.Ri"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IAssemblyNameProvider.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: 5eea837708474d7e9c1cb4b2eca0213f\ntimeCreated: 1580717710"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IFileIO.cs",
"chars": 529,
"preview": "using System.IO;\n\nnamespace Packages.Rider.Editor.ProjectGeneration\n{\n internal interface IFileIO\n {\n bool Exists(s"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IFileIO.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: 1bdab5b8331b4506bf4eae379235c053\ntimeCreated: 1580717666"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IGUIDGenerator.cs",
"chars": 134,
"preview": "namespace Packages.Rider.Editor.ProjectGeneration\n{\n internal interface IGUIDGenerator\n {\n string ProjectGuid(strin"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IGUIDGenerator.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: 8c7e8e301f8b4c30bbf9db502d637f0f\ntimeCreated: 1580717700"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IGenerator.cs",
"chars": 392,
"preview": "using System.Collections.Generic;\n\nnamespace Packages.Rider.Editor.ProjectGeneration\n{\n internal interface IGenerator\n "
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/IGenerator.cs.meta",
"chars": 84,
"preview": "fileFormatVersion: 2\nguid: 39cb9ab8a3b2452cbf58ffbea841d203\ntimeCreated: 1580717654"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/LastWriteTracker.cs",
"chars": 1630,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace Packages.Rider.Editor.Pro"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/LastWriteTracker.cs.meta",
"chars": 83,
"preview": "fileFormatVersion: 2\nguid: 7019e230344c48d4b81602e2e978e5de\ntimeCreated: 1645608955"
},
{
"path": "Packages/com.unity.ide.rider/Rider/Editor/ProjectGeneration/PackageManagerTracker.cs",
"chars": 1325,
"preview": "using System.IO;\n\n#if UNITY_2020_1_OR_NEWER\nusing UnityEditor.PackageManager;\n#endif\n\nnamespace Packages.Rider.Editor.Pr"
}
]
// ... and 104 more files (download for full content)
About this extraction
This page contains the full source code of the egametang/ET GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 304 files (856.8 KB), approximately 254.7k tokens, and a symbol index with 400 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.