Merge branch 'update-netcode-1.2'

This commit is contained in:
Brian Will 2024-07-14 23:04:08 -07:00
Родитель b9b34d3419 f58361f299
Коммит 26d5c778be
283 изменённых файлов: 6912 добавлений и 8736 удалений

55
NetcodeSamples/.gitignore поставляемый
Просмотреть файл

@ -1,55 +0,0 @@
**/Library/
**/UserSettings/
**/Temp/
**/obj/
**/.vscode/
**/.vs
**/Assets/Plugins/*
**/Assets/Plugins.meta
*.user
*.idea
*.csproj
*.csproj.user
*.sln
*.suo
*.userprefs
*.app
*.VC.*
.DS_Store
*.swp
*.DotSettings
*.gen.csproj.meta
obj.meta
.vs/
build/*
TwoStickShooter/Pure/Library/AnnotationManager
*.pyc
#generated by performance framework
*/PerformanceTestRunInfo.json
Performance/Assets/StreamingAssets.meta
Performance/Assets/StreamingAssets
StreamingAssets.meta
PerformanceTestRunInfo.json
PerformanceTestRunInfo.json.meta
**/InitTestScene*
**/Logs
**/Assets/StreamingAssets
**/Assets/EntityCache
**/Assets/TypeDependencyCache
**/Assets/SceneDependencyCache/*
**/Assets/SceneDependencyCache.meta
*~master*
*~HEAD*
artifacts
.Editor
.UnityLauncher.Editor

Просмотреть файл

@ -1,5 +0,0 @@
{
"tags": [],
"launchArguments": null,
"opened": 1685539680224
}

Просмотреть файл

@ -1,28 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-687ff696e4df441e5931eb81db4d1db0-93214019566545601-0",
"GlobalObjectId_V1-1-efd815fb310344e61bf5fa7dc0f06555-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "android",
"Configuration": 2
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"ENABLE_HYBRID_RENDERER_V2",
"FRONTEND_PLAYER_BUILD"
]
},
{
"$type": "Unity.Build.Classic.ClassicScriptingSettings, Unity.Build.Classic",
"ScriptingBackend": 1,
"Il2CppCompilerConfiguration": 1,
"UseIncrementalGC": false
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 47d3411d85eb04758a5dd2c7d57da3ed
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,29 +0,0 @@
{
"Show": true,
"Dependencies": [],
"Components": [
{
"$type": "Unity.Build.Common.GeneralSettings, Unity.Build",
"ProductName": "Asteroids",
"CompanyName": "Unity Technologies",
"Version": "0.1"
},
{
"$type": "Unity.Build.Common.SceneList, Unity.Build",
"$version": 1,
"BuildCurrentScene": false,
"SceneInfos": [
{
"Scene": "GlobalObjectId_V1-1-e1d9a388defac7049a631b0047b4278e-102900000-0",
"AutoLoad": true
}
]
},
{
"$type": "Unity.Build.Classic.ClassicScriptingSettings, Unity.Build.Classic",
"ScriptingBackend": 0,
"Il2CppCompilerConfiguration": 1,
"UseIncrementalGC": false
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 74cc8eb4a6f6148b8b85e86a7ac739f2
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,15 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-450f20c3e50de4631a862cc7b7da1ae6-93214019566545601-0",
"GlobalObjectId_V1-1-74cc8eb4a6f6148b8b85e86a7ac739f2-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "linux",
"Configuration": 2
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: aec82c4761a8e40beb219c8f2483abd1
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,15 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-69018010a3ed1485a922ec6aeaa79daf-93214019566545601-0",
"GlobalObjectId_V1-1-74cc8eb4a6f6148b8b85e86a7ac739f2-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "linux",
"Configuration": 2
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 17866e5cc4b0345a691647ebe95d40f0
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,15 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-450f20c3e50de4631a862cc7b7da1ae6-93214019566545601-0",
"GlobalObjectId_V1-1-74cc8eb4a6f6148b8b85e86a7ac739f2-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "macos",
"Configuration": 2
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 5d0038210168e4e7f90593725442bb39
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,15 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-69018010a3ed1485a922ec6aeaa79daf-93214019566545601-0",
"GlobalObjectId_V1-1-74cc8eb4a6f6148b8b85e86a7ac739f2-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "macos",
"Configuration": 2
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: d00c07af8c31e43838690cbe652e53ca
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,15 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-450f20c3e50de4631a862cc7b7da1ae6-93214019566545601-0",
"GlobalObjectId_V1-1-74cc8eb4a6f6148b8b85e86a7ac739f2-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "win",
"Configuration": 2
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 28c100126ec9540fd9f4dfe16d0a1de0
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,15 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-69018010a3ed1485a922ec6aeaa79daf-93214019566545601-0",
"GlobalObjectId_V1-1-74cc8eb4a6f6148b8b85e86a7ac739f2-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "win",
"Configuration": 2
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 5475da7dea6224981b641ce465030f6f
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,95 +0,0 @@
{
"Show": true,
"Dependencies": [],
"Components": [
{
"$type": "Unity.Build.Common.GeneralSettings, Unity.Build",
"ProductName": "NetcodeSamples",
"CompanyName": "Unity Technologies",
"Version": "0.1"
},
{
"$type": "Unity.Build.Common.SceneList, Unity.Build",
"$version": 1,
"BuildCurrentScene": false,
"SceneInfos": [
{
"Scene": "GlobalObjectId_V1-1-78517606c5abc4802a5a988a3f3fc2bf-102900000-0",
"AutoLoad": true
},
{
"Scene": "GlobalObjectId_V1-1-06ff0d515097f4e60973d2ca8d13eb39-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-e1d9a388defac7049a631b0047b4278e-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-46d38099d1fc14425a0f07eff0d05a93-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-87e39eaa89fb14d36836ac169c4259e0-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-d82373bbdea1f8744be2e30aed760957-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-998e01b18de7849b7ae73384062aa9d5-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-4cbe01b86844d4e719a3ad9d806e43ba-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-001253cdb73684a52b79be89e69df75b-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-5ad911e91463347e9885555d2d19e289-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-ebf120ae2dfed4ea9bb75f83b7ecdd1e-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-2418473ae6e6248b5803823efb4de8f5-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-ba6070794104a46da93b6b86088e3c01-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-9becba3d8791e41dc88d2151be0640f7-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-d775a53e92655c74f8ceb41730800e49-102900000-0",
"AutoLoad": false
},
{
"Scene": "GlobalObjectId_V1-1-bef98bc3729e842e5b2f2cc28f19931d-102900000-0",
"AutoLoad": false
}
]
},
{
"$type": "Unity.Build.Classic.ClassicScriptingSettings, Unity.Build.Classic",
"ScriptingBackend": 0,
"Il2CppCompilerConfiguration": 1,
"UseIncrementalGC": false
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"FRONTEND_PLAYER_BUILD"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: efd815fb310344e61bf5fa7dc0f06555
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,24 +0,0 @@
{
"Show": true,
"Dependencies": [],
"Components": [
{
"$type": "NetCodeConversionSettings, Unity.NetCode.Authoring.Hybrid",
"Target": 2
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"UNITY_CLIENT",
"ENABLE_HYBRID_RENDERER_V2"
]
},
{
"$type": "Unity.Entities.Conversion.ConversionSystemFilterSettings, Unity.Entities.Hybrid",
"ExcludedConversionSystemAssemblies": [
"GlobalObjectId_V1-1-b7d127909f4a04725b3edab4d8700bed-5897886265953266890-0",
"GlobalObjectId_V1-1-78161aee92fd144059f0fc99d4018d7b-5897886265953266890-0"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 450f20c3e50de4631a862cc7b7da1ae6
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,12 +0,0 @@
{
"Show": true,
"Dependencies": [],
"Components": [
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"ENABLE_HYBRID_RENDERER_V2"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 687ff696e4df441e5931eb81db4d1db0
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,60 +0,0 @@
{
"Show": true,
"Dependencies": [],
"Components": [
{
"$type": "Unity.Build.DotsRuntime.IL2CPPSettings, Unity.Build.DotsRuntime",
"ScriptDebugging": 0,
"WaitForDebugger": false
},
{
"$type": "NetCodeConversionSettings, Unity.NetCode.Authoring.Hybrid",
"Target": 1
},
{
"$type": "Unity.Build.DotsRuntime.DotsRuntimeRootAssembly, Unity.Build.DotsRuntime",
"BeeTargetOverride": null,
"RootAssembly": "GlobalObjectId_V1-1-89eead9c79a674e97a92f7769eaedc59-5897886265953266890-0"
},
{
"$type": "Unity.Entities.Conversion.ConversionSystemFilterSettings, Unity.Entities.Hybrid",
"ExcludedConversionSystemAssemblies": [
"GlobalObjectId_V1-1-7a450cf7ca9694b5a8bfa3fd83ec635a-5897886265953266890-0",
"GlobalObjectId_V1-1-c52403dffb9bc4f33a799f71eca4ab9a-5897886265953266890-0",
"GlobalObjectId_V1-1-8281b45ed5ac44d5b9f06391156a3007-5897886265953266890-0",
"GlobalObjectId_V1-1-308a44c6d7c754f2bb0e69df7e894c78-5897886265953266890-0"
]
},
{
"$type": "Unity.Build.Common.GeneralSettings, Unity.Build",
"ProductName": "AsteroidsServer-DotsRuntime",
"CompanyName": "Unity",
"Version": "1.0.0"
},
{
"$type": "Unity.Build.Common.SceneList, Unity.Build",
"$version": 1,
"BuildCurrentScene": false,
"SceneInfos": [
{
"Scene": "GlobalObjectId_V1-1-e1d9a388defac7049a631b0047b4278e-102900000-0",
"AutoLoad": true
}
]
},
{
"$type": "Unity.Entities.Runtime.Build.DotsRuntimeScriptingSettings, Unity.Entities.Runtime.Build",
"EnableSafetyChecks": 0,
"EnableProfiler": 0,
"ScriptingDefines": [
"UNITY_SERVER",
"UNITY_DOTSRUNTIME"
],
"EnableMultithreading": true
},
{
"$type": "Unity.Entities.Runtime.Build.DotsRuntimeBurstSettings, Unity.Entities.Runtime.Build",
"EnableBurst": true
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 9b3a397188c16436d8802a1edfb882cc
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,31 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-9b3a397188c16436d8802a1edfb882cc-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.DotsRuntime.DotsRuntimeBuildProfile, Unity.Build.DotsRuntime",
"Target": "linux-ns21",
"Configuration": 2,
"UseNewPipeline": false,
"PipelineConstructor": null
},
{
"$type": "Unity.Build.Common.GeneralSettings, Unity.Build",
"ProductName": "AsteroidsServer-DotsRuntime-Linux",
"CompanyName": "Unity",
"Version": "1.0.0"
},
{
"$type": "Unity.Entities.Runtime.Build.DotsRuntimeScriptingSettings, Unity.Entities.Runtime.Build",
"EnableSafetyChecks": 0,
"EnableProfiler": 0,
"ScriptingDefines": [
"UNITY_SERVER",
"UNITY_DOTSRUNTIME"
],
"EnableMultithreading": true
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 17ae86afc85e04d8d8b0e2003757b527
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,40 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-9b3a397188c16436d8802a1edfb882cc-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.DotsRuntime.DotsRuntimeBuildProfile, Unity.Build.DotsRuntime",
"Target": "macos-ns21",
"Configuration": 2,
"UseNewPipeline": false,
"PipelineConstructor": null
},
{
"$type": "Unity.Build.Common.GeneralSettings, Unity.Build",
"ProductName": "AsteroidsServer-DotsRuntime-MacOS",
"CompanyName": "Unity",
"Version": "1.0.0"
},
{
"$type": "Unity.Entities.Runtime.Build.DotsRuntimeBurstSettings, Unity.Entities.Runtime.Build",
"EnableBurst": true
},
{
"$type": "Unity.Build.DotsRuntime.IL2CPPSettings, Unity.Build.DotsRuntime",
"ScriptDebugging": 0,
"WaitForDebugger": false
},
{
"$type": "Unity.Entities.Runtime.Build.DotsRuntimeScriptingSettings, Unity.Entities.Runtime.Build",
"EnableSafetyChecks": 0,
"EnableProfiler": 0,
"ScriptingDefines": [
"UNITY_SERVER",
"UNITY_DOTSRUNTIME"
],
"EnableMultithreading": true
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: df8e7252145fd4e96846b97d32d75a48
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,31 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-9b3a397188c16436d8802a1edfb882cc-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.DotsRuntime.DotsRuntimeBuildProfile, Unity.Build.DotsRuntime",
"Target": "windows-ns21",
"Configuration": 2,
"UseNewPipeline": false,
"PipelineConstructor": null
},
{
"$type": "Unity.Build.Common.GeneralSettings, Unity.Build",
"ProductName": "AsteroidsServer-DotsRuntime-Win",
"CompanyName": "Unity",
"Version": "1.0.0"
},
{
"$type": "Unity.Entities.Runtime.Build.DotsRuntimeScriptingSettings, Unity.Entities.Runtime.Build",
"EnableSafetyChecks": 0,
"EnableProfiler": 0,
"ScriptingDefines": [
"UNITY_SERVER",
"UNITY_DOTSRUNTIME"
],
"EnableMultithreading": true
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 6b1f192ecffdb4fd6bae6124373e844b
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,22 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-687ff696e4df441e5931eb81db4d1db0-93214019566545601-0",
"GlobalObjectId_V1-1-efd815fb310344e61bf5fa7dc0f06555-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "macos",
"Configuration": 2
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"ENABLE_HYBRID_RENDERER_V2",
"FRONTEND_PLAYER_BUILD"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: bce786920f20443b899dc5caad373df4
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,28 +0,0 @@
{
"Show": true,
"Dependencies": [],
"Components": [
{
"$type": "NetCodeConversionSettings, Unity.NetCode.Authoring.Hybrid",
"Target": 1
},
{
"$type": "Unity.Build.Classic.EnableHeadlessMode, Unity.Build.Classic"
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"UNITY_SERVER"
]
},
{
"$type": "Unity.Entities.Conversion.ConversionSystemFilterSettings, Unity.Entities.Hybrid",
"ExcludedConversionSystemAssemblies": [
"GlobalObjectId_V1-1-7a450cf7ca9694b5a8bfa3fd83ec635a-5897886265953266890-0",
"GlobalObjectId_V1-1-c52403dffb9bc4f33a799f71eca4ab9a-5897886265953266890-0",
"GlobalObjectId_V1-1-8281b45ed5ac44d5b9f06391156a3007-5897886265953266890-0",
"GlobalObjectId_V1-1-308a44c6d7c754f2bb0e69df7e894c78-5897886265953266890-0"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 69018010a3ed1485a922ec6aeaa79daf
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,22 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-687ff696e4df441e5931eb81db4d1db0-93214019566545601-0",
"GlobalObjectId_V1-1-efd815fb310344e61bf5fa7dc0f06555-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "win",
"Configuration": 2
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"ENABLE_HYBRID_RENDERER_V2",
"FRONTEND_PLAYER_BUILD"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 07d34a3775a0a9348b6cf58a8b6daf54
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -1,33 +0,0 @@
{
"Show": true,
"Dependencies": [
"GlobalObjectId_V1-1-687ff696e4df441e5931eb81db4d1db0-93214019566545601-0",
"GlobalObjectId_V1-1-efd815fb310344e61bf5fa7dc0f06555-93214019566545601-0"
],
"Components": [
{
"$type": "Unity.Build.Classic.ClassicScriptingSettings, Unity.Build.Classic",
"ScriptingBackend": 1,
"Il2CppCompilerConfiguration": 1,
"UseIncrementalGC": false
},
{
"$type": "Unity.Build.Classic.ClassicBuildProfile, Unity.Build.Classic",
"$version": 1,
"Platform": "ios",
"Configuration": 2
},
{
"$type": "Unity.Build.Classic.ClassicCodeStrippingOptions, Unity.Build.Classic",
"StripEngineCode": false,
"ManagedStrippingLevel": 1
},
{
"$type": "Unity.Build.Common.PlayerScriptingDefines, Unity.Build",
"Defines": [
"ENABLE_HYBRID_RENDERER_V2",
"FRONTEND_PLAYER_BUILD"
]
}
]
}

Просмотреть файл

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: bc4e25bd6a15f40c69fd55121e903c05
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 627ef77bce55554428c21a56f59002bd, type: 3}

Просмотреть файл

@ -0,0 +1,17 @@
# global config file must have the is_global=true present in the first line.
is_global=true
# enabe/disable the Netcode source generator files output in the temp folder. 0 disable, empty or 1 enable.
unity.netcode.sourcegenerator.write_files_to_disk=0
# enable/disable Netcode source generator logs output to the Temp/NetCodeGenerated/sourcegenerato.log file. 0 disable, empty or 1 enable.
unity.netcode.sourcegenerator.write_logs_to_disk=0
# the default Netcode source generator logging level is error.
unity.netcode.sourcegenerator.logging_level=error
# Netcode source generator will emit profile timings. 0 disable, empty or 1 enable.
unity.netcode.sourcegenerator.emit_timing=0
# Netcode source generator will wait attaching the debugger before processing the specified assembly (or all if the value is empty). Keep commented to avoid the debugger to attach
#unity.netcode.sourcegenerator.attach_debugger=ASSEMBLY_NAME_OR_EMPTY

Просмотреть файл

@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: 89eead9c79a674e97a92f7769eaedc59
AssemblyDefinitionImporter:
guid: 438ceb2db4b2d4bb89ca202571b8606d
RoslynAnalyzerConfigImporter:
externalObjects: {}
userData:
assetBundleName:

Просмотреть файл

@ -1,28 +0,0 @@
# Netcode for Entities samples
- [Netcode for Entities Manual](https://docs.unity3d.com/Packages/com.unity.netcode@latest)
- [Netcode forums](https://forum.unity.com/forums/dots-netcode.425/)
### Asteroids
A small game featuring the Netcode for Entities Package features.
### NetCube
A small sample featuring the Netcode for Entities Package basic features, this is the sample used in the __Getting Started__ guide in the manual.
### PredictionSwitching
A sample using predicted physics based on Unity Physics. The sample is predicting all objects close the the player but not objects far away. The color of the spheres will change to indicate if they are predicted or interpolated.
### PlayerList
A sample which shows how to maintain a list of connected players by using the RPC feature.
### HelloNetcode
This is a suite of samples which aim to be small, simple and show features in isolation. Later samples then re-use earlier ones so it's simpler to see exactly what's being shown and similar routines (like connecting, going in game etc) don't need to be repeated. This way samples can also build on top of each other and become more complex. They are split into Basic, Intermediate and Advanced areas depending on level of complexity and how commonly the things shown are needed in a normal project.
## Building
Building is done via the **Build Settings** window as with normal Unity builds. Make sure you have the appropriate player type set int he _Entities->Build_ tab in the **Player Settings**. To build the whole sample scene list (with frontend) as a client/server build select the **ClientAndServer** as the *Netcode client target*. To make a client only build select **Client**. For a server only build switch to the **Dedicated Server** platform target. This will automatically use the **Server** configuration.

Просмотреть файл

@ -0,0 +1,15 @@
using UnityEngine;
using Unity.Entities;
using UnityEngine.UI;
public class ScoreTrackerAuthoring : MonoBehaviour
{
class Baker : Baker<ScoreTrackerAuthoring>
{
public override void Bake(ScoreTrackerAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.ManualOverride); // So it's not included in the relevancy radius check, as we default Score to always be relevant by default.
AddComponent(entity, new AsteroidScore());
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bbd786c2e47c4d9d916c41b6d613bedf
timeCreated: 1704924341

Просмотреть файл

@ -0,0 +1,17 @@
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
public partial class SetAlwaysRelevantSystem : SystemBase
{
protected override void OnCreate()
{
var relevancy = SystemAPI.GetSingletonRW<GhostRelevancy>();
// This is set OnCreate but can be updated at runtime as well
// You could also add an AlwaysRelevant component to mark entities at authoring time too
relevancy.ValueRW.DefaultRelevancyQuery = GetEntityQuery(typeof(AsteroidScore));
}
protected override void OnUpdate() { }
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b95edb1f46594018ad5b79019be30c23
timeCreated: 1704923615

Просмотреть файл

@ -84,12 +84,15 @@ namespace Asteroids.Client
{
if (SystemAPI.TryGetSingleton<CommandTarget>(out var commandTarget))
{
if (commandTarget.targetEntity == Entity.Null ||
!EntityManager.HasComponent<ShipCommandData>(commandTarget.targetEntity))
if (commandTarget.targetEntity == Entity.Null)
{
// No ghosts are spawned, so create a placeholder struct to store the commands in
var ent = EntityManager.CreateEntity();
EntityManager.AddBuffer<ShipCommandData>(ent);
// No ghosts are spawned, so we need to create a placeholder input component to store commands in.
// If the thin client timed out, and reconnected, we need to ensure this is not already created.
if (!SystemAPI.TryGetSingletonEntity<ShipCommandData>(out var ent))
{
ent = EntityManager.CreateEntity();
EntityManager.AddBuffer<ShipCommandData>(ent);
}
SystemAPI.SetSingleton(new CommandTarget{targetEntity = ent});
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c8a873b0609d413da434c85e89747b60
timeCreated: 1704928110

Просмотреть файл

@ -0,0 +1,31 @@
using System;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;
using UnityEngine.UI;
namespace Samples.Asteroids.Client.UI
{
public class ScoreUI : MonoBehaviour
{
Text m_ScoreTextToUpdate;
EntityQuery m_ScoreQuery;
void Start()
{
m_ScoreTextToUpdate = GetComponent<Text>();
}
void Update()
{
if (ClientServerBootstrap.ClientWorld == null)
return;
if (m_ScoreQuery == default)
m_ScoreQuery = ClientServerBootstrap.ClientWorld.EntityManager.CreateEntityQuery(typeof(AsteroidScore));
if (m_ScoreQuery.IsEmpty)
return;
var score = m_ScoreQuery.GetSingleton<AsteroidScore>().Value;
m_ScoreTextToUpdate.text = "Score: " + score;
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c9782b8d61f5475d8650cebbde4b409f
timeCreated: 1704928119

Просмотреть файл

@ -1,34 +0,0 @@
{
"name": "Asteroids",
"rootNamespace": "",
"references": [
"Asteroids.Server",
"Bootstrap",
"Unity.Burst",
"Unity.Entities",
"Unity.Mathematics",
"Unity.NetCode",
"Unity.Networking.Transport",
"Unity.Platforms.RunLoop",
"Unity.Platforms.Common",
"Unity.Scenes",
"Unity.Runtime.UnityInstance",
"Unity.Runtime.EntryPoint",
"Unity.Tiny.IO",
"Configuration",
"SamplesCommon.Mixed",
"Unity.Logging",
"Unity.Collections"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [
"UNITY_DOTS_ENTRYPOINT"
],
"versionDefines": [],
"noEngineReferences": false
}

Просмотреть файл

@ -1,41 +0,0 @@
#if UNITY_DOTSRUNTIME
using Unity.Platforms;
using Unity.Runtime;
using Unity.Runtime.EntryPoint;
using Unity.Logging;
namespace DOTSRuntime.Server
{
public class ServerMain
{
public static void Main()
{
Program.Initialize();
var unity = UnityInstance.Initialize();
TempMemoryScope.EnterScope();
Unity.Logging.DefaultSettings.Initialize();
Log.Info("Logging initialized...");
TempMemoryScope.ExitScope();
unity.OnTick = (double timestampInSeconds) =>
{
UnityInstance.UpdatePreFrame();
Unity.Logging.DefaultSettings.UpdateFunction();
var shouldContinue = unity.Update(timestampInSeconds);
UnityInstance.UpdatePostFrame(shouldContinue);
return shouldContinue;
};
PlatformEvents.OnQuit += (sender, evt) =>
{
Log.Info("Application is shutting down...");
unity.Deinitialize();
Unity.Logging.Internal.LoggerManager.FlushAll();
Program.Shutdown();
};
RunLoop.EnterMainLoop(unity.OnTick);
}
}
}
#endif

Просмотреть файл

@ -1,4 +1,3 @@
using AOT;
using Unity.Burst;
using Unity.Burst.Intrinsics;
using Unity.Collections;
@ -23,7 +22,7 @@ public struct RpcLevelLoaded : IComponentData, IRpcCommandSerializer<RpcLevelLoa
}
[BurstCompile(DisableDirectCall = true)]
[MonoPInvokeCallback(typeof(RpcExecutor.ExecuteDelegate))]
[AOT.MonoPInvokeCallback(typeof(RpcExecutor.ExecuteDelegate))]
private static void InvokeExecute(ref RpcExecutor.Parameters parameters)
{
var rpcData = default(RpcLevelLoaded);

Просмотреть файл

@ -0,0 +1,8 @@
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;
public struct AsteroidScore : IComponentData
{
[GhostField] public int Value;
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7662c530971f45df8fd9ec535d44d9a1
timeCreated: 1704924454

Просмотреть файл

@ -40,6 +40,9 @@ public struct LevelComponent : IComponentData
[UnityEngine.SerializeField] private byte _enableGhostImportanceScaling;
public bool enableGhostImportanceScaling => _enableGhostImportanceScaling != 0;
[UnityEngine.SerializeField] private byte _useBatchScalingFunction;
public bool useBatchScalingFunction => _useBatchScalingFunction != 0;
public static LevelComponent Default = new LevelComponent
{
levelWidth = 2048,

Просмотреть файл

@ -0,0 +1,82 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1525771447942938581
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3563239003391643041}
- component: {fileID: 2744348672411674421}
- component: {fileID: 8151184371318657797}
- component: {fileID: 4477279557710362359}
m_Layer: 0
m_Name: ScoreTracker
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3563239003391643041
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1525771447942938581}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 405.97098, y: 156.42908, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2744348672411674421
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1525771447942938581}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bbd786c2e47c4d9d916c41b6d613bedf, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &8151184371318657797
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1525771447942938581}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c16549610bfe4458aa9389201d072bb6, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &4477279557710362359
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1525771447942938581}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7c79d771cedb4794bf100ce60df5f764, type: 3}
m_Name:
m_EditorClassIdentifier:
DefaultGhostMode: 1
SupportedGhostModes: 2
OptimizationMode: 0
Importance: 1
prefabId:
HasOwner: 0
SupportAutoCommandTarget: 1
TrackInterpolationDelay: 0
GhostGroup: 0
UsePreSerialization: 1

Просмотреть файл

@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: ba70763071fb74d6ea02dd2c0dd614b6
TextScriptImporter:
guid: f997563c14c5b441a8f27a57be296294
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:

Просмотреть файл

@ -27,12 +27,12 @@ namespace Asteroids.Server
float m_AsteroidRadius;
float m_ShipRadius;
private NativeReference<Random> randomReference;
ComponentLookup<PlayerStateComponentData> playerStateFromEntity;
ComponentLookup<CommandTarget> commandTargetFromEntity;
ComponentLookup<NetworkId> networkIdFromEntity;
ComponentLookup<LocalTransform> localTransformLookup;
[BurstCompile]
public void OnCreate(ref SystemState state)
{
var builder = new EntityQueryBuilder(Allocator.Temp).WithAll<LocalTransform, ShipStateComponentData>();
@ -63,12 +63,24 @@ namespace Asteroids.Server
state.RequireForUpdate(m_LevelQuery);
state.RequireForUpdate<AsteroidsSpawner>();
// Ensure every random is seeded uniquely (not Burst compatible)...
// AND that the random feedbacks into itself, ensuring better quality randomness for Asteroid spawns.
var fileTimeUtc = System.DateTime.UtcNow.ToFileTimeUtc();
randomReference = new NativeReference<Random>(Random.CreateFromIndex((uint) fileTimeUtc), Allocator.Persistent);
playerStateFromEntity = state.GetComponentLookup<PlayerStateComponentData>();
commandTargetFromEntity = state.GetComponentLookup<CommandTarget>();
networkIdFromEntity = state.GetComponentLookup<NetworkId>();
localTransformLookup = state.GetComponentLookup<LocalTransform>(true);
}
[BurstCompile]
public void OnDestroy(ref SystemState state)
{
randomReference.Dispose();
// Others are disposed automatically.
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
@ -132,7 +144,6 @@ namespace Asteroids.Server
state.Dependency = JobHandle.CombineDependencies(state.Dependency, combinedDependencies);
var tick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
var random = new Random(tick.SerializedData);
SystemAPI.TryGetSingleton<ClientServerTickRate>(out var tickRate);
tickRate.ResolveDefaults();
@ -165,7 +176,7 @@ namespace Asteroids.Server
dynamicAsteroidEntities = dynamicAsteroidEntities,
staticAsteroidEntities = staticAsteroidEntities,
level = level,
random = random,
random = randomReference,
tick = tick,
shipPrefab = m_ShipPrefab,
fixedDeltaTime = fixedDeltaTime,
@ -183,7 +194,7 @@ namespace Asteroids.Server
shipTransformsList = shipTransformsList,
localTransformLookup = localTransformLookup,
level = level,
random = random,
random = randomReference,
tick = tick,
asteroidPrefab = m_AsteroidPrefab,
asteroidLevelPadding = asteroidLevelPadding,
@ -207,6 +218,7 @@ namespace Asteroids.Server
}
[BurstCompile]
[WithAll(typeof(PlayerSpawnRequest))]
internal partial struct SpawnPlayerShips : IJobEntity
{
public EntityCommandBuffer ecb;
@ -221,7 +233,7 @@ namespace Asteroids.Server
[ReadOnly] public NativeList<Entity> staticAsteroidEntities;
[ReadOnly] public NativeList<LevelComponent> level;
public Random random;
public NativeReference<Random> random;
public NetworkTick tick;
public Entity shipPrefab;
public float fixedDeltaTime;
@ -229,7 +241,7 @@ namespace Asteroids.Server
public float minShipAsteroidSpawnDistance;
public float minShipToShipSpawnDistance;
void Execute(Entity entity, in PlayerSpawnRequest request, in ReceiveRpcCommandRequest requestSource)
void Execute(Entity entity, in ReceiveRpcCommandRequest requestSource)
{
// Destroy the spawn request:
ecb.DestroyEntity(entity);
@ -243,7 +255,9 @@ namespace Asteroids.Server
// Try find a random spawn position for the Ship that isn't near another player.
// Don't allow failure though, just take a "bad" position instead.
TryFindSpawnPos(ref random, shipTransforms.AsArray(), level[0], shipLevelPadding, minShipToShipSpawnDistance, out var validShipPos);
var rand = random.Value;
TryFindSpawnPos(ref rand, shipTransforms.AsArray(), level[0], shipLevelPadding, minShipToShipSpawnDistance, out var validShipPos);
random.Value = rand;
// Instantiate ship:
var shipEntity = ecb.Instantiate(shipPrefab);
@ -293,7 +307,7 @@ namespace Asteroids.Server
[ReadOnly] public ComponentLookup<LocalTransform> localTransformLookup;
[ReadOnly] public NativeList<LevelComponent> level;
public Random random;
public NativeReference<Random> random;
public NetworkTick tick;
public Entity asteroidPrefab;
public float asteroidLevelPadding;
@ -305,15 +319,16 @@ namespace Asteroids.Server
public void Execute()
{
var currentNumAsteroids = staticAsteroidEntities.Length + dynamicAsteroidEntities.Length;
var rand = random.Value;
for (int i = currentNumAsteroids; i < numAsteroids; ++i)
{
// Spawn asteroid at random pos, assuming we can find a valid one that isn't under a ship.
// Don't treat this as an error (because it may happen occasionally by chance, or if the map is packed with ships).
// Instead, just stop attempting to spawn any more this frame.
if (!TryFindSpawnPos(ref random, shipTransformsList.AsArray(), level[0], asteroidLevelPadding, minShipAsteroidSpawnDistance, out var validAsteroidPos))
return;
if (!TryFindSpawnPos(ref rand, shipTransformsList.AsArray(), level[0], asteroidLevelPadding, minShipAsteroidSpawnDistance, out var validAsteroidPos))
break;
var angle = random.NextFloat(-0.0f, 359.0f);
var angle = rand.NextFloat(-0.0f, 359.0f);
//@ronald. this is necessary since the meshes are not backing the correct scaling factor
var originalScale = localTransformLookup[asteroidPrefab].Scale;
var trans = LocalTransform.FromPositionRotationScale(
@ -335,6 +350,8 @@ namespace Asteroids.Server
else
ecb.SetComponent(e, vel);
}
random.Value = rand;
}
}

Просмотреть файл

@ -3,8 +3,10 @@ using Unity.Burst;
using Unity.Burst.Intrinsics;
using Unity.Entities;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Transforms;
using Unity.NetCode;
@ -80,6 +82,8 @@ namespace Asteroids.Server
commandTarget = state.GetComponentLookup<CommandTarget>();
linkedEntityGroupFromEntity = state.GetBufferLookup<LinkedEntityGroup>();
state.RequireForUpdate<AsteroidScore>();
}
[BurstCompile]
@ -102,6 +106,8 @@ namespace Asteroids.Server
[ReadOnly] public EntityTypeHandle entityType;
[ReadOnly] public NativeList<LevelComponent> level;
[NativeSetThreadIndex] public int ThreadIndex;
[NativeDisableParallelForRestriction] public NativeArray<int> asteroidDestructCounter;
public NetworkTick tick;
public float fixedDeltaTime;
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
@ -163,6 +169,7 @@ namespace Asteroids.Server
if (Intersect(firstRadius, secondRadius, firstPos, secondPos))
{
commandBuffer.DestroyEntity(unfilteredChunkIndex, asteroidEntity);
asteroidDestructCounter[ThreadIndex]++;
if(level[0].bulletsDestroyedOnContact)
commandBuffer.DestroyEntity(unfilteredChunkIndex, bulletEntities[bullet]);
@ -335,6 +342,26 @@ namespace Asteroids.Server
}
}
[BurstCompile]
internal struct GatherAsteroidDestructCounter : IJob
{
[ReadOnly] public NativeArray<int> asteroidDestructCounter;
public EntityCommandBuffer commandBuffer;
[ReadOnly] public int currentScore;
[ReadOnly] public Entity scoreSingleton;
public void Execute()
{
int total = currentScore;
for (int i = 1; i < asteroidDestructCounter.Length; ++i)
{
total += asteroidDestructCounter[i];
}
commandBuffer.SetComponent(scoreSingleton, new AsteroidScore { Value = total });
}
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
@ -361,6 +388,11 @@ namespace Asteroids.Server
commandTarget.Update(ref state);
linkedEntityGroupFromEntity.Update(ref state);
int chunkCount = asteroidQuery.CalculateChunkCountWithoutFiltering();
int maxThreadCount = JobsUtility.ThreadIndexCount;
var asteroidDestroyCounter = CollectionHelper.CreateNativeArray<int>(maxThreadCount, Allocator.TempJob);
var asteroidJob = new DestroyAsteroidJob
{
@ -369,6 +401,7 @@ namespace Asteroids.Server
bulletAgeType = bulletAgeType,
transformType = transformType,
asteroidDestructCounter = asteroidDestroyCounter,
staticAsteroidType = staticAsteroidType,
sphereType = sphereType,
@ -407,14 +440,26 @@ namespace Asteroids.Server
var h1 = asteroidJob.ScheduleParallel(asteroidQuery, asteroidDep);
var h2 = shipJob.ScheduleParallel(shipQuery, shipDep);
var handle = JobHandle.CombineDependencies(h1, h2);
var cleanupShipJob = new ClearShipPointerJob
{
playerClearQueue = playerClearQueue,
commandTarget = commandTarget,
linkedEntityGroupFromEntity = linkedEntityGroupFromEntity
};
var asteroidScore = SystemAPI.GetSingleton<AsteroidScore>();
var updateScore = new GatherAsteroidDestructCounter
{
commandBuffer = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged),
asteroidDestructCounter = asteroidDestroyCounter,
currentScore = asteroidScore.Value,
scoreSingleton = SystemAPI.GetSingletonEntity<AsteroidScore>()
};
var scoreHandle = updateScore.Schedule(dependsOn: h1);
var cleanupHandle = asteroidDestroyCounter.Dispose(scoreHandle);
var handle = JobHandle.CombineDependencies(h1, h2, cleanupHandle);
state.Dependency = JobHandle.CombineDependencies(cleanupShipJob.Schedule(h2), handle);
}

Просмотреть файл

@ -18,6 +18,7 @@ namespace Asteroids.Server
{
private EntityQuery m_LevelGroup;
private PortableFunctionPointer<GhostImportance.ScaleImportanceDelegate> m_ScaleFunctionPointer;
private PortableFunctionPointer<GhostImportance.BatchScaleImportanceDelegate> m_BatchScaleFunction;
public void OnCreate(ref SystemState state)
{
@ -26,6 +27,7 @@ namespace Asteroids.Server
state.RequireForUpdate<ServerSettings>();
m_ScaleFunctionPointer = GhostDistanceImportance.ScaleFunctionPointer;
m_BatchScaleFunction = GhostDistanceImportance.BatchScaleFunctionPointer;
}
[BurstCompile]
@ -56,6 +58,7 @@ namespace Asteroids.Server
});
state.EntityManager.AddComponentData(gridSingleton, new GhostImportance
{
BatchScaleImportanceFunction = settings.levelData.useBatchScalingFunction ? m_BatchScaleFunction: default,
ScaleImportanceFunction = m_ScaleFunctionPointer,
GhostConnectionComponentType = ComponentType.ReadOnly<GhostConnectionPosition>(),
GhostImportanceDataType = ComponentType.ReadOnly<GhostDistanceData>(),

Просмотреть файл

@ -20,7 +20,6 @@ GameObject:
- component: {fileID: -6897639722122874107}
- component: {fileID: -5231493766772932575}
- component: {fileID: 5941306285269308673}
- component: {fileID: -4792171066699290668}
- component: {fileID: -4874576295782791584}
- component: {fileID: 4277607168032350576}
m_Layer: 0
@ -247,19 +246,6 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 700954011552825696}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
--- !u!114 &-4792171066699290668
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 700954011552825696}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 54b6467bcd530cd4d809fae70ea0ca9d, type: 3}
m_Name:
m_EditorClassIdentifier:
color: {r: 0, g: 0, b: 0, a: 0}
--- !u!114 &-4874576295782791584
MonoBehaviour:
m_ObjectHideFlags: 0

Просмотреть файл

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 40ce81ce095fd4c4eaef9cb266786fb4
guid: ac23f599aae3a47c7893493141f25d33
folderAsset: yes
DefaultImporter:
externalObjects: {}

Просмотреть файл

@ -0,0 +1,86 @@
using Unity.Entities;
using Unity.NetCode;
namespace Samples.CustomChunkSerializer
{
[GhostEnabledBit]
[GhostComponent(SendDataForChildEntity = true)]
public struct IntCompo1 : IComponentData, IEnableableComponent
{
[GhostField] public int Value;
}
[GhostEnabledBit]
[GhostComponent(SendDataForChildEntity = true)]
public struct IntCompo2 : IComponentData, IEnableableComponent
{
[GhostField] public int Value;
}
[GhostEnabledBit]
[GhostComponent(SendDataForChildEntity = true)]
public struct IntCompo3 : IComponentData, IEnableableComponent
{
[GhostField] public int Value;
}
[GhostEnabledBit]
[GhostComponent(SendDataForChildEntity = true)]
public struct FloatCompo1 : IComponentData, IEnableableComponent
{
[GhostField(Quantization = 1000)] public float Value;
}
[GhostEnabledBit]
[GhostComponent(SendDataForChildEntity = true)]
public struct FloatCompo2 : IComponentData, IEnableableComponent
{
[GhostField(Quantization = 1000)] public float Value;
}
[GhostEnabledBit]
[GhostComponent(SendDataForChildEntity = true)]
public struct FloatCompo3 : IComponentData, IEnableableComponent
{
[GhostField(Quantization = 1000)] public float Value;
}
[GhostComponent(SendDataForChildEntity = true)]
public struct Buf1 : IBufferElementData
{
[GhostField] public int Value;
}
[GhostComponent(SendDataForChildEntity = true)]
public struct Buf2 : IBufferElementData
{
[GhostField] public float Value;
}
[GhostComponent(SendDataForChildEntity = true)]
public struct Buf3 : IBufferElementData
{
[GhostField] public int Value1;
[GhostField] public int Value2;
[GhostField] public float Value3;
[GhostField] public float Value4;
}
[GhostComponent(SendTypeOptimization = GhostSendType.OnlyInterpolatedClients)]
public struct InterpolatedOnlyComp : IComponentData
{
[GhostField] public int Value1;
[GhostField] public int Value2;
[GhostField] public int Value3;
[GhostField] public int Value4;
}
[GhostComponent(OwnerSendType = SendToOwnerType.SendToNonOwner)]
public struct OwnerOnlyComp : IComponentData
{
[GhostField] public int Value1;
[GhostField] public int Value2;
[GhostField] public int Value3;
[GhostField] public int Value4;
}
}

Просмотреть файл

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b7730bbe03dd347f688216bd8ec25b05
guid: 741840051872d4ba6824aa3e7f449ccc
MonoImporter:
externalObjects: {}
serializedVersion: 2

Просмотреть файл

@ -0,0 +1,367 @@
using System;
using AOT;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
using Unity.NetCode.LowLevel.Unsafe;
using Unity.Transforms;
using UnityEngine.Assertions;
namespace Samples.CustomChunkSerializer
{
[BurstCompile]
public struct ChunkSerializer
{
//About the snapshot buffer memory layout
//The snapshot buffer contains entries in this format
// [tick][ent1 data][ent2 data] ... [entN data]
// the data has the following layout:
// uint Tick
// 4 bytes aligned change mask bits
// (optional)
// 4 bytes aligned enable bits state
// padding (to 16 bytes)
// component1 (aligned to 16 byte boundary)
// component2 (aligned to 16 byte boundary)
// ..
// componentN (aligned to 16 byte boundary)
//About the bitsize memory layout.
//The bitStartAndSize contains the start uint and bit len of the ghost data.
//Has the following layout
// [Component1 ][Component2
// |ent1 start|ent1 len|..|entN start|entN len||ent1 start|ent1 len|..|entN start|entN len|
//
// The stride is NumComponent * (endIndex - startIndex), where endIndex and startIndex are the first and last
// relevant entity index in the chunk.
public static PortableFunctionPointer<GhostPrefabCustomSerializer.CollectComponentDelegate> CollectComponentFunc =
new PortableFunctionPointer<GhostPrefabCustomSerializer.CollectComponentDelegate>(CollectComponents);
public static PortableFunctionPointer<GhostPrefabCustomSerializer.ChunkSerializerDelegate> SerializerFunc =
new PortableFunctionPointer<GhostPrefabCustomSerializer.ChunkSerializerDelegate>(SerializeChunk);
public static PortableFunctionPointer<GhostPrefabCustomSerializer.ChunkPreserializeDelegate> PreSerializerFunc =
new PortableFunctionPointer<GhostPrefabCustomSerializer.ChunkPreserializeDelegate>(PreSerializeChunk);
//Custom method to register the component types in a specific order, so that the chunk serializer can be written
//way more easily.
[BurstCompile(DisableDirectCall = true)]
[MonoPInvokeCallback(typeof(GhostPrefabCustomSerializer.CollectComponentDelegate))]
public static void CollectComponents(IntPtr componentTypesPtr, IntPtr componentCountPtr)
{
ref var componentTypes = ref GhostComponentSerializer.TypeCast<NativeList<ComponentType>>(componentTypesPtr);
ref var componentCount = ref GhostComponentSerializer.TypeCast<NativeArray<int>>(componentCountPtr);
//Root
componentTypes.Add(ComponentType.ReadWrite<GhostOwner>());
componentTypes.Add(ComponentType.ReadWrite<LocalTransform>());
componentTypes.Add(ComponentType.ReadWrite<IntCompo1>());
componentTypes.Add(ComponentType.ReadWrite<IntCompo2>());
componentTypes.Add(ComponentType.ReadWrite<IntCompo3>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo1>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo2>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo3>());
componentTypes.Add(ComponentType.ReadWrite<InterpolatedOnlyComp>());
componentTypes.Add(ComponentType.ReadWrite<OwnerOnlyComp>());
componentTypes.Add(ComponentType.ReadWrite<Buf1>());
componentTypes.Add(ComponentType.ReadWrite<Buf2>());
componentTypes.Add(ComponentType.ReadWrite<Buf3>());
componentCount[0] = 13;
//Child 1
componentTypes.Add(ComponentType.ReadWrite<IntCompo1>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo1>());
componentTypes.Add(ComponentType.ReadWrite<Buf1>());
componentCount[1] = 3;
//Child 2
componentTypes.Add(ComponentType.ReadWrite<IntCompo2>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo2>());
componentTypes.Add(ComponentType.ReadWrite<Buf2>());
componentCount[2] = 3;
}
[BurstCompile(DisableDirectCall = true)]
[MonoPInvokeCallback(typeof(GhostPrefabCustomSerializer.CollectComponentDelegate))]
private static unsafe void PreSerializeChunk(in ArchetypeChunk chunk, in GhostCollectionPrefabSerializer typeData,
in DynamicBuffer<GhostCollectionComponentIndex> componentIndices,
ref GhostPrefabCustomSerializer.Context context)
{
var indices = (GhostCollectionComponentIndex*)componentIndices.GetUnsafeReadOnlyPtr() + typeData.FirstComponent;
CopyComponentsToSnapshot(chunk, ref context, typeData, indices);
}
const int BaselinesPerEntity = 4;
//Assumptions made:
// - components are never removed (so we not check for presence)
[BurstCompile(DisableDirectCall = true)]
[MonoPInvokeCallback(typeof(GhostPrefabCustomSerializer.ChunkSerializerDelegate))]
private static unsafe void SerializeChunk(ref ArchetypeChunk chunk,
in GhostCollectionPrefabSerializer typeData,
in DynamicBuffer<GhostCollectionComponentIndex> componentIndices,
ref GhostPrefabCustomSerializer.Context context,
ref DataStreamWriter tempWriter,
in StreamCompressionModel compressionModel, ref int lastSerializedEntity)
{
var baselinesPerEntity = (IntPtr*)context.baselinePerEntityPtr;
var sameBaselinePerEntity = (int*)context.sameBaselinePerEntityPtr;
int* entityBitAndSize = (int*)context.entityStartBit;
var entityCount = context.endIndex - context.startIndex;
int* componentBitsSize = entityBitAndSize + 2*entityCount;
int compBitSizeStride = 2*entityCount;
var indices = (GhostCollectionComponentIndex*)componentIndices.GetUnsafeReadOnlyPtr() + typeData.FirstComponent;
if(context.hasPreserializedData == 0)
CopyComponentsToSnapshot(chunk, ref context, typeData, indices);
int* dynamicDataSizePerEntity = (int*)context.dynamicDataSizePerEntityPtr;
for (int ent = context.startIndex; ent < context.endIndex; ++ent)
{
var old = tempWriter;
var entOffset = ent - context.startIndex;
//Avoid serializing irrelevant entities
var sameBaselineCount = sameBaselinePerEntity[entOffset];
if (sameBaselineCount < 0)
{
// This is an irrelevant ghost, do not send . There is no need to reset the
//bits size and start bit, because the same check is also done outside.
continue;
}
//The writer contains data in a different format in the case of the chunk serializer:
//we are serializing on per "entity" directly here.
//This give the advantage of being able to early exit without need to serialise other entities if
//they don't fit (or at least early exiting after the first one that fails)
//Ideally, (this is a second phase), we can now write directly in the real data stream. Right now
//this is not possible because we are adding the size of buffers and ghosts data (delta compressed) before
//the ghost stream.
var snapshotData = context.snapshotDataPtr + entOffset*context.snapshotStride;
var changeMaskData = snapshotData + sizeof(int);
var currentWrittenBits = tempWriter.LengthInBits;
var baseline0Ptr = baselinesPerEntity[BaselinesPerEntity*entOffset];
var baseline1Ptr = baselinesPerEntity[BaselinesPerEntity*entOffset + 1];
var baseline2Ptr = baselinesPerEntity[BaselinesPerEntity*entOffset + 2];
//This can an IntPtrZero if there are no buffers of the baseline does not exist
var dynamicDataBaselinePtr = baselinesPerEntity[BaselinesPerEntity*entOffset + 3];
//This requires to overwrite the snapshot data with all zeros or keep the current predicted baseline (optimal)
var ghostSendType = GhostSendType.AllClients;
var sendToOwner = SendToOwnerType.All;
//the comp bit size already point to the entityBitSize[1] for the first component entry.
var compBitSize = componentBitsSize + 2*entOffset + 1;
if (typeData.PredictionOwnerOffset != 0)
{
var isOwner = (context.networkId == *(int*)((byte*)snapshotData + typeData.PredictionOwnerOffset));
if (typeData.PartialSendToOwner != 0)
sendToOwner = isOwner ? SendToOwnerType.SendToOwner : SendToOwnerType.SendToNonOwner;
if (typeData.PartialComponents != 0 && typeData.OwnerPredicted != 0)
ghostSendType = isOwner ? GhostSendType.OnlyPredictedClients : GhostSendType.OnlyInterpolatedClients;
}
if (baseline2Ptr != IntPtr.Zero)
{
SerializeWithThreeBaselines(snapshotData, context.snapshotOffset, sendToOwner, ghostSendType,
indices, ref tempWriter, compressionModel, baseline0Ptr, baseline1Ptr, baseline2Ptr,
changeMaskData, context.snapshotDynamicDataPtr, dynamicDataBaselinePtr,
ref dynamicDataSizePerEntity[entOffset], compBitSize, compBitSizeStride);
}
//Single baseline
else if (baseline0Ptr != IntPtr.Zero)
{
SerializeWithSingleBaseline(snapshotData, context.snapshotOffset, sendToOwner, ghostSendType,
indices, ref tempWriter, compressionModel, baseline0Ptr,
changeMaskData, context.snapshotDynamicDataPtr, dynamicDataBaselinePtr,
ref dynamicDataSizePerEntity[entOffset], compBitSize, compBitSizeStride);
}
//No baseline, we are passing a pointer to a baseline that contains all zero.
//The advantage of this is that can be extended, to allow using "initial-value"
//optimization, to send the delta in respect the prefab initial data.
else
{
SerializeWithSingleBaseline(snapshotData, context.snapshotOffset, sendToOwner, ghostSendType,
indices, ref tempWriter, compressionModel, context.zeroBaseline,
changeMaskData, context.snapshotDynamicDataPtr, IntPtr.Zero,
ref dynamicDataSizePerEntity[entOffset], compBitSize, compBitSizeStride);
}
//Count the number of bits for each ghosts.
entityBitAndSize[2*entOffset] = currentWrittenBits / 32;
entityBitAndSize[2*entOffset+1] = tempWriter.LengthInBits - currentWrittenBits;
var missing = 32 - tempWriter.LengthInBits & 31;
if (missing < 32)
tempWriter.WriteRawBits(0, missing);
if (tempWriter.HasFailedWrites)
{
//If we were able to store at least one entity there is not need to mark the stream
//as failed.
//Rollback it here and let the outer loop to serialize the full entities.
if (entOffset > 0)
{
tempWriter = old;
lastSerializedEntity = ent - 1;
}
break;
}
}
}
private static unsafe void CopyComponentsToSnapshot(ArchetypeChunk chunk,
ref GhostPrefabCustomSerializer.Context context,
in GhostCollectionPrefabSerializer typeData,
GhostCollectionComponentIndex* indices)
{
var ghostChunkComponentTypesPtr = (DynamicComponentTypeHandle*)context.ghostChunkComponentTypes;
var maskOffset = 0;
var snapshotOffset = context.snapshotOffset;
var dynamicSnapshotOffset = context.dynamicDataOffset;
var changeMaskUints = GhostComponentSerializer.ChangeMaskArraySizeInUInts(typeData.ChangeMaskBits);
var snapshotPtr = context.snapshotDataPtr;
var enableBits = (byte*)(snapshotPtr + sizeof(int) + 4*changeMaskUints);
//ROOT COMPONENTS
//GENERATE ONLY THIS
new Unity.NetCode.Generated.GhostOwnerGhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr, indices[0], snapshotPtr, ref snapshotOffset);
new Unity.NetCode.Generated.TransformDefaultVariantGhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[1], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[2].ComponentIndex], enableBits, ref maskOffset);
new CustomSerializer.Generated.IntCompo1GhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr, indices[2], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[3].ComponentIndex], enableBits, ref maskOffset);
new CustomSerializer.Generated.IntCompo2GhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr, indices[3], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[4].ComponentIndex], enableBits, ref maskOffset);
new CustomSerializer.Generated.IntCompo3GhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[4], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[5].ComponentIndex], enableBits, ref maskOffset);
new CustomSerializer.Generated.FloatCompo1GhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[5], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[6].ComponentIndex], enableBits, ref maskOffset);
new CustomSerializer.Generated.FloatCompo2GhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[6], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[7].ComponentIndex], enableBits, ref maskOffset);
new CustomSerializer.Generated.FloatCompo3GhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[7], snapshotPtr, ref snapshotOffset);
new CustomSerializer.Generated.InterpolatedOnlyCompGhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[8], snapshotPtr, ref snapshotOffset);
new CustomSerializer.Generated.OwnerOnlyCompGhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[9], snapshotPtr, ref snapshotOffset);
new CustomSerializer.Generated.Buf1GhostComponentSerializer().CopyBufferToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[10], snapshotPtr, ref snapshotOffset, ref dynamicSnapshotOffset);
new CustomSerializer.Generated.Buf2GhostComponentSerializer().CopyBufferToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[11], snapshotPtr, ref snapshotOffset, ref dynamicSnapshotOffset);
new CustomSerializer.Generated.Buf3GhostComponentSerializer().CopyBufferToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[12], snapshotPtr, ref snapshotOffset, ref dynamicSnapshotOffset);
//CHILD COMPONENTS
var linkedGroup = chunk.GetBufferAccessor(ref context.linkedEntityGroupTypeHandle);
for (int ent = context.startIndex; ent < context.endIndex; ++ent)
{
var childEnableMaskOffset = maskOffset;
var childSnapshotOffset = snapshotOffset;
//GENERATE ONLY THIS
var childEnt = linkedGroup[ent][1].Value;
var childEntityStorageInfo = context.childEntityLookup[childEnt];
CustomGhostSerializerHelpers.CopyEnableBits(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, childEntityStorageInfo.IndexInChunk+1, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[13].ComponentIndex], enableBits, ref childEnableMaskOffset);
new CustomSerializer.Generated.IntCompo1GhostComponentSerializer().CopyChildComponentToSnapshot(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, ref context,
ghostChunkComponentTypesPtr,indices[13], snapshotPtr, ref childSnapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, childEntityStorageInfo.IndexInChunk+1, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[14].ComponentIndex], enableBits, ref childEnableMaskOffset);
new CustomSerializer.Generated.FloatCompo1GhostComponentSerializer().CopyChildComponentToSnapshot(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, ref context,
ghostChunkComponentTypesPtr,indices[14], snapshotPtr, ref childSnapshotOffset);
new CustomSerializer.Generated.Buf1GhostComponentSerializer().CopyChildBufferToSnapshot(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, ref context,
ghostChunkComponentTypesPtr,indices[15], snapshotPtr, ref childSnapshotOffset, ref dynamicSnapshotOffset);
childEnt = linkedGroup[ent][2].Value;
childEntityStorageInfo = context.childEntityLookup[childEnt];
CustomGhostSerializerHelpers.CopyEnableBits(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, childEntityStorageInfo.IndexInChunk+1, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[16].ComponentIndex], enableBits, ref childEnableMaskOffset);
new CustomSerializer.Generated.IntCompo2GhostComponentSerializer().CopyChildComponentToSnapshot(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, ref context,
ghostChunkComponentTypesPtr,indices[16], snapshotPtr, ref childSnapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, childEntityStorageInfo.IndexInChunk+1, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[17].ComponentIndex], enableBits, ref childEnableMaskOffset);
new CustomSerializer.Generated.FloatCompo2GhostComponentSerializer().CopyChildComponentToSnapshot(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, ref context,
ghostChunkComponentTypesPtr,indices[17], snapshotPtr, ref childSnapshotOffset);
new CustomSerializer.Generated.Buf2GhostComponentSerializer().CopyChildBufferToSnapshot(childEntityStorageInfo.Chunk, childEntityStorageInfo.IndexInChunk, ref context,
ghostChunkComponentTypesPtr,indices[18], snapshotPtr, ref childSnapshotOffset, ref dynamicSnapshotOffset);
Assert.IsTrue(childEnableMaskOffset <= typeData.EnableableBits);
snapshotPtr += context.snapshotStride;
enableBits += context.snapshotStride;
}
}
private static unsafe void SerializeWithSingleBaseline(IntPtr snapshotData, int snapshotOffset,
SendToOwnerType sendToOwnerMask, GhostSendType sendTypeMask,
GhostCollectionComponentIndex* indices,
ref DataStreamWriter writer, in StreamCompressionModel compressionModel, IntPtr baseline0Ptr,
IntPtr changeMaskData,
IntPtr dynamicSnapshotData, IntPtr baselineDynamicData, ref int dynamicDataSizePerEntity,
int* compBitSize, int compBitSizeStride)
{
var changeMaskOffset = 0;
//GENERATE ONLY THIS
compBitSize[0*compBitSizeStride] = default(Unity.NetCode.Generated.GhostOwnerGhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[1*compBitSizeStride] = default(Unity.NetCode.Generated.TransformDefaultVariantGhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[2*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo1GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[3*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo2GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[4*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo3GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[5*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo1GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[6*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo2GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[7*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo3GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[8*compBitSizeStride] = default(CustomSerializer.Generated.InterpolatedOnlyCompGhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel, (int)(indices[8].SendMask & sendTypeMask) | (int)(indices[8].SendToOwner & sendToOwnerMask));
compBitSize[9*compBitSizeStride] = default(CustomSerializer.Generated.OwnerOnlyCompGhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel, (int)(indices[9].SendMask & sendTypeMask) | (int)(indices[9].SendToOwner & sendToOwnerMask));
compBitSize[10*compBitSizeStride] = default(CustomSerializer.Generated.Buf1GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[11*compBitSizeStride] = default(CustomSerializer.Generated.Buf2GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[12*compBitSizeStride] = default(CustomSerializer.Generated.Buf3GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[13*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo1GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[14*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo1GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[15*compBitSizeStride] = default(CustomSerializer.Generated.Buf1GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[16*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo2GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[17*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo2GhostComponentSerializer).SerializeComponentSingleBaseline(snapshotData,baseline0Ptr,changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref writer, compressionModel);
compBitSize[18*compBitSizeStride] = default(CustomSerializer.Generated.Buf2GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
}
private static unsafe void SerializeWithThreeBaselines(IntPtr snapshotData, int snapshotOffset,
SendToOwnerType sendToOwnerMask, GhostSendType sendTypeMask,
GhostCollectionComponentIndex* indices,
ref DataStreamWriter writer, in StreamCompressionModel compressionModel,
IntPtr baseline0Ptr, IntPtr baseline1Ptr, IntPtr baseline2Ptr, IntPtr changeMaskData,
IntPtr dynamicSnapshotData, IntPtr baselineDynamicData, ref int dynamicDataSizePerEntity,
int* compBitSize, int compBitSizeStride)
{
var predictor = new GhostDeltaPredictor(
new NetworkTick { SerializedData = GhostComponentSerializer.TypeCast<uint>(snapshotData) },
new NetworkTick { SerializedData = GhostComponentSerializer.TypeCast<uint>(baseline0Ptr) },
new NetworkTick { SerializedData = GhostComponentSerializer.TypeCast<uint>(baseline1Ptr) },
new NetworkTick { SerializedData = GhostComponentSerializer.TypeCast<uint>(baseline2Ptr) });
var changeMaskOffset = 0;
//GENERATE ONLY THIS
compBitSize[0*compBitSizeStride] = default(Unity.NetCode.Generated.GhostOwnerGhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[1*compBitSizeStride] = default(Unity.NetCode.Generated.TransformDefaultVariantGhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[2*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo1GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[3*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo2GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[4*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo3GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[5*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo1GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[6*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo2GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[7*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo3GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[8*compBitSizeStride] = default(CustomSerializer.Generated.InterpolatedOnlyCompGhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel, (int)(indices[8].SendMask & sendTypeMask) | (int)(indices[8].SendToOwner & sendToOwnerMask));
compBitSize[9*compBitSizeStride] = default(CustomSerializer.Generated.OwnerOnlyCompGhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel, (int)(indices[9].SendMask & sendTypeMask) | (int)(indices[9].SendToOwner & sendToOwnerMask));
compBitSize[10*compBitSizeStride] = default(CustomSerializer.Generated.Buf1GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[11*compBitSizeStride] = default(CustomSerializer.Generated.Buf2GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[12*compBitSizeStride] = default(CustomSerializer.Generated.Buf3GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
compBitSize[13*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo1GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[14*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo1GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[15*compBitSizeStride] = default(CustomSerializer.Generated.Buf1GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity,ref writer, compressionModel);
compBitSize[16*compBitSizeStride] = default(CustomSerializer.Generated.IntCompo2GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[17*compBitSizeStride] = default(CustomSerializer.Generated.FloatCompo2GhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,baseline1Ptr, baseline2Ptr, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
compBitSize[18*compBitSizeStride] = default(CustomSerializer.Generated.Buf2GhostComponentSerializer).SerializeBuffer(snapshotData,baseline0Ptr, dynamicSnapshotData, baselineDynamicData, changeMaskData,ref changeMaskOffset, ref snapshotOffset, ref dynamicDataSizePerEntity, ref writer, compressionModel);
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd27dcb4859d4bed9b64765463602e5d
timeCreated: 1695464432

Просмотреть файл

@ -0,0 +1,23 @@
{
"name": "CustomSerializer",
"rootNamespace": "",
"references": [
"GUID:953adc2a6b8b4e3c8df5b728bcd546e9",
"GUID:734d92eba21c94caba915361bd5ac177",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:e0cd26848372d4e5c891c569017e11f1",
"GUID:2665a8d13d1b3f18800f46e256720795",
"GUID:f2d49d9fa7e7eb3418e39723a7d3b92f",
"GUID:8819f35a0fc84499b990e90a4ca1911f",
"GUID:a5baed0c9693541a5bd947d336ec7659"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 54ee788d33434308ae727b0388cbe753
timeCreated: 1695581462

Просмотреть файл

@ -0,0 +1,165 @@
# Creating a custom chunk serialization method
This sample shows how implement and register a custom serialization method, to serialize ghost chunks of a given archetype.
## Why using custom serialization function
Only one reason: **Peformance**! When using the a custom serialization function designed for a specific archetype,
you can make (or relax) certain assumptions: I.e.
* No checks for removed components.
* Optimise the loops that gather and copy data.
* Generally write things in a way that gives both the compiler, and burst in primis, more opportunities for
auto-vectorization.
In general, it also gives a CPU performance gain in cases where the ghost has a lots of small components, reducing function pointer call overhead (and more importantly single call setup overhead).
### Current limitation of the custom serializer API
This is an advanced feature of the Netcode package (we can say it is "in-preview"), available for now only when using the PrefabCreation API (i.e. manually created ghost entities types).
# USING THE API
## Registering custom serializers
In order to use custom chunk serialization function; **said function must be registered to the GhostCollection system, before the
prefabs are collected and processed.** A good place to put the registration, is in the system where you create the prefabs.
To register your custom serialization function, you need to retrieve the `GhostCollectionCustomSerializer` singleton,
and add an entry for a given archetype. A ghost archetype is identified by its GhostType hash.
```csharp
GhostPrefabCreation.ConvertToGhostPrefab(EntityManager, prefab, prefabConfig);
var hash = (Unity.Entities.Hash128)EntityManager.GetComponentData<GhostType>(prefab);
var customSerializers = SystemAPI.GetSingletonRW<GhostCollectionCustomSerializers>();
customSerializers.ValueRW.Serializers.Add(hash, new GhostPrefabCustomSerializer
{
SerializeChunk = CustomChunkSerializer.SerializerFunc,
PreSerializeChunk = CustomChunkSerializer.PreSerializerFunc
});
```
Both `SerializeChunk` and `PreSerializeChunk` functions are optional, meaning that you can have either one of them, or both.
## Register a custom component list provider for the archetype
In order to be able to serialize a component, it is necessary to access and retrieve the component data from the chunk.
This is achieved by accessing the list of serialized components registered for this archetype, and retrieve from them a list of type handles (the `DynamicTypeHandle`) for each component type.
Each the prefab processed by the `GhostCollectionSystem` has:
- one entry in the `GhostCollectionPrefabSerializer` list, that contains a bunch of metadata information to serialize the type.
- each serialized component will create an entry in the `GhostCollectionComponentIndex` (for root and child entities in order) that contains (among other things):
- which serializer to use (the SerializerIndex field)
- an index in the `DynamicTypeList` (ComponentIndex) that let you retrieve the `DynamicComponentTypeHandle`.
```csharp
var typeData = ghostCollectionPrefabSerializer[ghostType];
var componentIndex = typeData.FirstComponent;
var index = componentIndices[componentIndex];
var dynamicTypeHandle = dynamicTypeList[index.ComponentIndex];
```
When writing a custom serialization method, it can be tricky to find the index of a specific component type
in the `componentIndices` list, for the current ghost archetype. That because a components position in the list depends upon:
- its stable-type-hash
- the serializer hash associated with that specific component for that archetype.
Therefore, we support passing a `CollectComponents` function pointer to the GhostPrefabCreation.ConvertToGhostPrefab` (a field of the config argument),
such that you know exactly the index into the `componentIndices` list for a specific component type.
For example:
```csharp
public static void CollectComponents(IntPtr componentTypesPtr, IntPtr componentCountPtr)
{
ref var componentTypes = ref GhostComponentSerializer.TypeCast<NativeList<ComponentType>>(componentTypesPtr);
ref var componentCount = ref GhostComponentSerializer.TypeCast<NativeArray<int>>(componentCountPtr);
//Root
componentTypes.Add(ComponentType.ReadWrite<GhostOwner>());
componentTypes.Add(ComponentType.ReadWrite<LocalTransform>());
componentTypes.Add(ComponentType.ReadWrite<IntCompo1>());
componentTypes.Add(ComponentType.ReadWrite<IntCompo2>());
componentTypes.Add(ComponentType.ReadWrite<IntCompo3>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo1>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo2>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo3>());
componentTypes.Add(ComponentType.ReadWrite<InterpolatedOnlyComp>());
componentTypes.Add(ComponentType.ReadWrite<OwnerOnlyComp>());
componentTypes.Add(ComponentType.ReadWrite<Buf1>());
componentTypes.Add(ComponentType.ReadWrite<Buf2>());
componentTypes.Add(ComponentType.ReadWrite<Buf3>());
componentCount[0] = 13;
//Child 1
componentTypes.Add(ComponentType.ReadWrite<IntCompo1>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo1>());
componentTypes.Add(ComponentType.ReadWrite<Buf1>());
componentCount[1] = 3;
//Child 2
componentTypes.Add(ComponentType.ReadWrite<IntCompo2>());
componentTypes.Add(ComponentType.ReadWrite<FloatCompo2>());
componentTypes.Add(ComponentType.ReadWrite<Buf2>());
componentCount[2] = 3;
}
```
You can find this function in the `CustomChunkSerializer.cs` file.
When the function is present, the components list for that archetype use the order specified by that method, giving you the possibility
to access the component types information consistently, and also allowing you to define the component serialization order.
## Implementing custom chunk serializer
We provide a bunch of utility methods that can be used to serialize the enable bits, buffers and components inside the
`CustomGhostSerializerHelpers` class.
The `CustomChunkSerializer.cs` can be considered a sort of template that you can reuse to write or generate your serializer almost
automatically (by just invoking the provider helper functions).
To make the code as re-usable as possible (and reducing mistakes), you must re-use the generated component serializer struct
(i.e `MyAssembly.Generated.MyComponentGhostComponentSerializer`) and:
- invoke on an instance of that serializer one of the `CopyToSnapshot<T>` methods.
- invoke on an instance of that serializer one of the `SerializeWithSingleBaseline` or `SerializeWithThreeBaseline` or `SerializeBuffer`.
Unfortunately, given that the `MyAssembly.Generated.MyComponentGhostComponentSerializer` serializer is auto-generated, most IDE's will not help auto-complete the method name, nor will they recognize the classes existence. But the code will compile correctly.
The serialization is divided in two steps:
### STEP 1 - COPY TO SNAPSHOT
You just need to implement the `CustomChunkSerializer.CopyComponentsToSnapshot` method, by copying the enable bits (if necessary), and copying the component data. For example:
```csharp
new Unity.NetCode.Generated.GhostOwnerGhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr, indices[0], snapshotPtr, ref snapshotOffset);
new Unity.NetCode.Generated.TransformDefaultVariantGhostComponentSerializer().CopyComponentToSnapshot(chunk, ref context,
ghostChunkComponentTypesPtr,indices[1], snapshotPtr, ref snapshotOffset);
CustomGhostSerializerHelpers.CopyEnableBits(chunk, context.startIndex, context.endIndex, context.snapshotStride,
ref ghostChunkComponentTypesPtr[indices[2].ComponentIndex], enableBits, ref maskOffset);
```
Similarly, for child entity components, we have similar methods that just scaffold some implementation details and boilerplate template code.
### STEP 2 - SERIALIZE TO THE DATASTREAM
After the data has been copied into the snapshot buffer, we can serialize the snapshot (entity by entity), into the data stream. Based on the acknowledge baselines, we should serialize by either:
- A single or default baseline
- three baselines.
The `CustomChunkSerializer` class has two methods that need to be implemented:
- `SerializeWithSingleBaseline`
- `SerializeWithThreeBaseline`
All code-generated serializers provide three static methods can be used for writing the snapshot data into the stream:
- `SerializeSingleBaseline`: serialize the data using only one baseline (either acked or the default one).
- `SerializeThreeBaseline`: serialize the data using the last three baselines (all acked) and by predicting the value to reduce the delta.
- `SerializeBuffer`: serialize a buffer to the stream using one single baseline (either the default or the acked one).
See the `CustomChunkSerializer.cs` for an example how to use them. But as a short example:
```csharp
//With a single baseline
compBitSize[0*compBitSizeStride] = default(Unity.NetCode.Generated.GhostOwnerGhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,
baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
//With three baseline
compBitSize[0*compBitSizeStride] = default(Unity.NetCode.Generated.GhostOwnerGhostComponentSerializer).SerializeComponentThreeBaseline(snapshotData,baseline0Ptr,
baseline1Ptr, baseline2Ptr, changeMaskData, ref changeMaskOffset, ref snapshotOffset, ref predictor, ref writer, compressionModel);
```

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 704a06de9d09493f8f217485272238a4
timeCreated: 1698317036

Просмотреть файл

@ -0,0 +1,233 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Transforms;
using Random = UnityEngine.Random;
namespace Samples.CustomChunkSerializer
{
[UpdateInGroup(typeof(InitializationSystemGroup))]
public partial class CustomSerializerSample : SystemBase
{
protected override void OnCreate()
{
RequireForUpdate<GhostCollection>();
RequireForUpdate<TestCustomSerializer>();
}
public static Entity CreateEntityArchetype(EntityManager entityManager, bool initValues)
{
var entity = entityManager.CreateEntity();
var child0 = entityManager.CreateEntity(typeof(GhostChildEntity));
var child1 = entityManager.CreateEntity(typeof(GhostChildEntity));
var linkedEntityGroups = entityManager.AddBuffer<LinkedEntityGroup>(entity);
linkedEntityGroups.Add(entity);
linkedEntityGroups.Add(child0);
linkedEntityGroups.Add(child1);
entityManager.AddComponent(entity, ComponentType.ReadWrite<GhostOwner>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<LocalTransform>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<IntCompo1>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<IntCompo2>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<IntCompo3>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<FloatCompo1>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<FloatCompo2>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<FloatCompo3>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<InterpolatedOnlyComp>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<OwnerOnlyComp>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<Buf1>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<Buf2>());
entityManager.AddComponent(entity, ComponentType.ReadWrite<Buf3>());
entityManager.AddComponent(child0, ComponentType.ReadWrite<IntCompo1>());
entityManager.AddComponent(child0, ComponentType.ReadWrite<FloatCompo1>());
entityManager.AddComponent(child0, ComponentType.ReadWrite<Buf1>());
entityManager.AddComponent(child1, ComponentType.ReadWrite<IntCompo2>());
entityManager.AddComponent(child1, ComponentType.ReadWrite<FloatCompo2>());
entityManager.AddComponent(child1, ComponentType.ReadWrite<Buf2>());
if (initValues)
{
entityManager.SetComponentData(entity, new IntCompo1 { Value = 100 });
entityManager.SetComponentData(entity, new IntCompo2 { Value = 200 });
entityManager.SetComponentData(entity, new IntCompo3 { Value = 300 });
entityManager.SetComponentData(entity, new FloatCompo1 { Value = 10f });
entityManager.SetComponentData(entity, new FloatCompo2 { Value = 20f });
entityManager.SetComponentData(entity, new FloatCompo3 { Value = 30f });
entityManager.SetComponentData(entity, new InterpolatedOnlyComp{Value1 = 10, Value2 = 20, Value3 = 30, Value4 = 40});
entityManager.SetComponentData(entity, new OwnerOnlyComp{Value1 = 10, Value2 = 20, Value3 = 30, Value4 = 40});
entityManager.SetComponentData(entity, LocalTransform.FromPosition(new float3(1f, 2f, 3f)));
var buf1 = entityManager.GetBuffer<Buf1>(entity);
buf1.ResizeUninitialized(5);
for (int i = 0; i < buf1.Length; ++i)
buf1.ElementAt(i).Value = 10;
var buf2 = entityManager.GetBuffer<Buf2>(entity);
buf2.ResizeUninitialized(5);
for (int i = 0; i < buf2.Length; ++i)
buf2.ElementAt(i).Value = 20;
var buf3 = entityManager.GetBuffer<Buf3>(entity);
buf3.ResizeUninitialized(5);
for (int i = 0; i < buf3.Length; ++i)
{
buf3.ElementAt(i).Value1 = 10;
buf3.ElementAt(i).Value2 = 20;
buf3.ElementAt(i).Value3 = 30f;
buf3.ElementAt(i).Value4 = 40f;
}
entityManager.SetComponentData(child0, new IntCompo1 { Value = 100 });
entityManager.SetComponentData(child0, new FloatCompo1 { Value = 100f });
buf1 = entityManager.GetBuffer<Buf1>(child0);
buf1.ResizeUninitialized(5);
for (int i = 0; i < buf1.Length; ++i)
buf1.ElementAt(i).Value = 10;
entityManager.SetComponentData(child1, new IntCompo2 { Value = 200 });
entityManager.SetComponentData(child1, new FloatCompo2 { Value = 200f });
buf2 = entityManager.GetBuffer<Buf2>(child1);
buf2.ResizeUninitialized(5);
for (int i = 0; i < buf1.Length; ++i)
buf2.ElementAt(i).Value = 20;
}
return entity;
}
protected override void OnUpdate()
{
//First frame: create all the ghost prefabs. This is going to create
Entity prefab = CreateEntityArchetype(EntityManager, World.IsServer());
var comp = SystemAPI.GetSingleton<TestCustomSerializer>();
var prefabConfig = new GhostPrefabCreation.Config
{
Name = "SimpleGhost",
Importance = 1,
SupportedGhostModes = GhostModeMask.All,
DefaultGhostMode = GhostMode.OwnerPredicted,
OptimizationMode = GhostOptimizationMode.Dynamic,
UsePreSerialization = comp.usePreserialization,
CollectComponentFunc = ChunkSerializer.CollectComponentFunc
};
EntityManager.SetName(prefab, prefabConfig.Name);
GhostPrefabCreation.ConvertToGhostPrefab(EntityManager, prefab, prefabConfig);
var hash = (Unity.Entities.Hash128)EntityManager.GetComponentData<GhostType>(prefab);
var customSerializer = SystemAPI.GetSingletonRW<GhostCollectionCustomSerializers>();
customSerializer.ValueRW.Serializers.Add(hash, new GhostPrefabCustomSerializer
{
SerializeChunk = ChunkSerializer.SerializerFunc,
PreSerializeChunk = ChunkSerializer.PreSerializerFunc
});
if (World.IsServer())
{
SystemAPI.GetSingletonRW<GhostSendSystemData>().ValueRW.UseCustomSerializer = comp.useCustomSerializer ? 1 : 0;
var entities = EntityManager.Instantiate(prefab, comp.numInstances, Allocator.Temp);
for (int i = 0; i < entities.Length; ++i)
{
EntityManager.SetComponentData(entities[i], new GhostOwner{NetworkId = 1});
}
}
Enabled = false;
}
}
[RequireMatchingQueriesForUpdate]
public partial class AutoGoInGame : SystemBase
{
protected override void OnCreate()
{
RequireForUpdate<GhostCollection>();
RequireForUpdate<TestCustomSerializer>();
}
protected override void OnUpdate()
{
var buffer = new EntityCommandBuffer(Allocator.Temp);
foreach (var (con,ent) in SystemAPI.Query<RefRO<NetworkId>>().WithNone<NetworkStreamInGame>().WithEntityAccess())
{
buffer.AddComponent(ent, new NetworkStreamInGame());
}
buffer.Playback(EntityManager);
}
}
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
[CreateAfter(typeof(GhostSendSystem))]
public partial class RandomizeSystem : SystemBase
{
protected override void OnCreate()
{
RequireForUpdate<TestCustomSerializer>();
}
protected override void OnUpdate()
{
//change all the components is a bad case scenario, because that may not let the serialization
//code to perform all the calculation (many call, zero size to write), lots of write etc
//it is possible to maximize the amount of work by forcing the ghost send system to always send
//up to x chunks to have some more reliable "results"
var testCustomSerializer = SystemAPI.GetSingleton<TestCustomSerializer>();
var ghostsQuery = GetEntityQuery(typeof(GhostInstance));
var ghostsChunks = ghostsQuery.ToArchetypeChunkArray(Allocator.Temp);
var entityTypeHandle = GetEntityTypeHandle();
foreach(var c in ghostsChunks)
{
if (UnityEngine.Random.value > testCustomSerializer.percentChunkChange)
continue;
var entities = c.GetNativeArray(entityTypeHandle);
for (var ent = 0; ent < entities.Length; ++ent)
{
if (UnityEngine.Random.value > testCustomSerializer.percentEntityChanges)
continue;
var entity = entities[ent];
var lg = EntityManager.GetBuffer<LinkedEntityGroup>(entity);
World.EntityManager.SetComponentData(entity, new IntCompo1{Value = Random.Range(-10, 20)});
World.EntityManager.SetComponentData(entity, new IntCompo2{Value = Random.Range(-10, 20)});
World.EntityManager.SetComponentData(entity, new IntCompo3{Value = Random.Range(-10, 20)});
World.EntityManager.SetComponentData(entity, new FloatCompo1{Value = Random.Range(-10f, 20f)});
World.EntityManager.SetComponentData(entity, new FloatCompo2{Value = Random.Range(-10f, 20f)});
World.EntityManager.SetComponentData(entity, new FloatCompo3{Value = Random.Range(-10f, 20f)});
{
var b1 = EntityManager.GetBuffer<Buf1>(lg[0].Value);
for (int el = 0; el < 5; ++el)
b1.ElementAt(el).Value = Random.Range(-10, 20);
}
{
var b1 = EntityManager.GetBuffer<Buf2>(lg[0].Value);
for (int el = 0; el < 5; ++el)
b1.ElementAt(el).Value = Random.Range(-10, 20);
}
{
var b1 = EntityManager.GetBuffer<Buf3>(lg[0].Value);
for (int el = 0; el < 5; ++el)
{
b1.ElementAt(el).Value1 = Random.Range(-10, 20);
b1.ElementAt(el).Value2 = Random.Range(-10, 20);
b1.ElementAt(el).Value3 = Random.Range(-10, 20);
b1.ElementAt(el).Value4 = Random.Range(-10, 20);
}
}
var child0 = lg[1].Value;
World.EntityManager.SetComponentData(child0, new IntCompo1{Value = Random.Range(-10, 20)});
World.EntityManager.SetComponentData(child0, new FloatCompo1{Value = Random.Range(-10f, 20f)});
{
var b1 = EntityManager.GetBuffer<Buf1>(lg[1].Value);
for (int el = 0; el < 5; ++el)
b1.ElementAt(el).Value = Random.Range(-10, 20);
}
var child1 = lg[2].Value;
World.EntityManager.SetComponentData(child1, new IntCompo2{Value = Random.Range(-10, 20)});
World.EntityManager.SetComponentData(child1, new FloatCompo2{Value = Random.Range(-10f, 20f)});
{
var b1 = EntityManager.GetBuffer<Buf2>(lg[2].Value);
for (int el = 0; el < 5; ++el)
b1.ElementAt(el).Value = Random.Range(-10, 20);
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7e1a5fbe08244f31abfa48fd361d61c8
timeCreated: 1695636910

Просмотреть файл

@ -0,0 +1,35 @@
using Unity.Entities;
using UnityEngine;
public struct TestCustomSerializer : IComponentData
{
public int numInstances;
public float percentChunkChange;
public float percentEntityChanges;
public bool useCustomSerializer;
public bool usePreserialization;
}
public class TestCustomSerializerAuthoring : MonoBehaviour
{
public int numInstances;
public int percentChunkChange;
public int percentEntityChanges;
public bool useCustomSerializer;
public bool usePreserialization;
private class Baker : Unity.Entities.Baker<TestCustomSerializerAuthoring>
{
public override void Bake(TestCustomSerializerAuthoring customSerializerAuthoring)
{
AddComponent(GetEntity(TransformUsageFlags.None), new TestCustomSerializer
{
numInstances = customSerializerAuthoring.numInstances,
percentChunkChange = customSerializerAuthoring.percentChunkChange*0.01f,
percentEntityChanges = customSerializerAuthoring.percentEntityChanges*0.01f,
useCustomSerializer = customSerializerAuthoring.useCustomSerializer,
usePreserialization = customSerializerAuthoring.usePreserialization,
});
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8a0aa95684d6490989bfb89e13358141
timeCreated: 1695846542

Просмотреть файл

@ -25,6 +25,11 @@ namespace Samples.HelloNetcode
// The initialize method is what entities calls to create the default worlds
public override bool Initialize(string defaultWorldName)
{
// If the user added an OverrideDefaultNetcodeBootstrap MonoBehaviour to their active scene,
// or disabled Bootstrapping project-wide, we should respect that here.
if (!DetermineIfBootstrappingEnabled())
return false;
#if UNITY_EDITOR
// If we are in the editor, we check if the loaded scene is "Frontend",
// if we are in a player we assume it is in the frontend if FRONTEND_PLAYER_BUILD
@ -41,7 +46,7 @@ namespace Samples.HelloNetcode
{
// This will enable auto connect. We only enable auto connect if we are not going through frontend.
// The frontend will parse and validate the address before connecting manually.
// Using this auto connect feature will deal with the client only connect address from Multiplayer PlayMode Tools
// Using this auto connect feature will deal with the client only connect address from PlayMode Tools
AutoConnectPort = 7979;
// Use "-port 8000" when running a build from commandline to specify the port to use
@ -51,7 +56,7 @@ namespace Samples.HelloNetcode
AutoConnectPort = UInt16.Parse(commandPort);
// Create the default client and server worlds, depending on build type in a player or the Multiplayer PlayMode Tools in the editor
// Create the default client and server worlds, depending on build type in a player or the PlayMode Tools in the editor
CreateDefaultClientServerWorlds();
}
else

Просмотреть файл

@ -40,20 +40,17 @@ namespace Samples.HelloNetcode
public void Start()
{
foreach (var world in World.All)
if (ClientServerBootstrap.ClientWorld != null)
{
if (world.IsClient() && !world.IsThinClient())
{
var sys = world.GetOrCreateSystemManaged<FrontendHUDSystem>();
sys.UIBehaviour = this;
var simGroup = world.GetExistingSystemManaged<SimulationSystemGroup>();
simGroup.AddSystemToUpdateList(sys);
}
var sys = ClientServerBootstrap.ClientWorld.GetOrCreateSystemManaged<FrontendHUDSystem>();
sys.UIBehaviour = this;
var simGroup = ClientServerBootstrap.ClientWorld.GetExistingSystemManaged<SimulationSystemGroup>();
simGroup.AddSystemToUpdateList(sys);
}
// We must always have an event system (DOTS-7177), but some scenes will already have one,
// so we only enable ours if we can't find someone else's.
if (FindObjectOfType<UnityEngine.EventSystems.EventSystem>(false) == null)
if (Object.FindFirstObjectByType<UnityEngine.EventSystems.EventSystem>() == null)
m_EventSystem.gameObject.SetActive(true);
}
}

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
@ -9,22 +11,40 @@ namespace Samples.HelloNetcode
void Start()
{
#if UNITY_SERVER
string sceneName = "Asteroids";
string defaultSceneName = "Asteroids";
#else
string sceneName = "Frontend";
string defaultSceneName = "Frontend";
#endif
// Commandline always overrides defaults if it exists
string commandScene = CommandLineUtils.GetCommandLineValueFromKey("scene");
if (!string.IsNullOrEmpty(commandScene))
if (string.IsNullOrWhiteSpace(commandScene))
{
var scene = SceneManager.GetSceneByName(commandScene);
if (!scene.IsValid())
Debug.LogWarning($"Scene '{commandScene}' not found, using default '{sceneName}' instead");
SceneManager.LoadScene(defaultSceneName);
return;
}
for (int i = 0; i < SceneManager.sceneCountInBuildSettings; ++i)
{
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
var scene = Path.GetFileNameWithoutExtension(scenePath);
if (commandScene == scene)
{
SceneManager.LoadScene(sceneName);
SceneManager.LoadScene(commandScene);
return;
}
}
Debug.LogError($"${commandScene} not found. Scenes present in the build\n: {string.Join(',', GetAllScenesInBuild())}");
Application.Quit(-1);
}
private IEnumerable<string> GetAllScenesInBuild()
{
for (int i = 0; i < SceneManager.sceneCountInBuildSettings; ++i)
{
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
yield return Path.GetFileNameWithoutExtension(scenePath);
}
}
}
}

Просмотреть файл

@ -25,9 +25,7 @@ namespace Samples.HelloNetcode
NetworkEndpoint m_Endpoint;
NetworkConnection m_ClientConnection;
public RelayServerData RelayClientData;
#if !UNITY_SERVER
public RelayFrontend UIBehaviour;
#endif
[Flags]
enum ClientStatus

Просмотреть файл

@ -25,9 +25,7 @@ namespace Samples.HelloNetcode
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial class HostServer : SystemBase
{
#if !UNITY_SERVER
public RelayFrontend UIBehaviour;
#endif
const int RelayMaxConnections = 5;
public string JoinCode;
@ -48,7 +46,6 @@ namespace Samples.HelloNetcode
SigningIn,
FailedToHost,
Ready,
GettingRegions,
Allocating,
GettingJoinCode,
GetRelayData,
@ -99,15 +96,7 @@ namespace Samples.HelloNetcode
#if !UNITY_SERVER
UIBehaviour.HostConnectionStatus = "Logging in anonymously";
#endif
m_HostStatus = WaitForSignIn(m_SignInTask, out m_RegionsTask);
return;
}
case HostStatus.GettingRegions:
{
#if !UNITY_SERVER
UIBehaviour.HostConnectionStatus = "Waiting for regions";
#endif
m_HostStatus = WaitForRegions(m_RegionsTask, out m_AllocationTask);
m_HostStatus = WaitForSignIn(m_SignInTask, out m_AllocationTask);
return;
}
case HostStatus.Allocating:
@ -140,11 +129,11 @@ namespace Samples.HelloNetcode
}
}
static HostStatus WaitForSignIn(Task signInTask, out Task<List<Region>> regionTask)
static HostStatus WaitForSignIn(Task signInTask, out Task<Allocation> allocationTask)
{
allocationTask = default;
if (!signInTask.IsCompleted)
{
regionTask = default;
return HostStatus.SigningIn;
}
@ -152,13 +141,13 @@ namespace Samples.HelloNetcode
{
Debug.LogError("Signing in failed");
Debug.LogException(signInTask.Exception);
regionTask = default;
return HostStatus.FailedToHost;
}
// Request list of valid regions
regionTask = RelayService.Instance.ListRegionsAsync();
return HostStatus.GettingRegions;
// Request an allocation to the Relay service
// with a maximum of 5 peer connections, for a maximum of 6 players.
allocationTask = RelayService.Instance.CreateAllocationAsync(RelayMaxConnections);
return HostStatus.Allocating;
}
static HostStatus WaitForInitialization(Task initializeTask, out Task nextTask)
@ -249,32 +238,6 @@ namespace Samples.HelloNetcode
return HostStatus.GettingJoinCode;
}
static HostStatus WaitForRegions(Task<List<Region>> collectRegionTask, out Task<Allocation> allocationTask)
{
if (!collectRegionTask.IsCompleted)
{
allocationTask = null;
return HostStatus.GettingRegions;
}
if (collectRegionTask.IsFaulted)
{
Debug.LogError("List regions request failed");
Debug.LogException(collectRegionTask.Exception);
allocationTask = null;
return HostStatus.FailedToHost;
}
var regionList = collectRegionTask.Result;
// pick a region from the list
var targetRegion = regionList[0].Id;
// Request an allocation to the Relay service
// with a maximum of 5 peer connections, for a maximum of 6 players.
allocationTask = RelayService.Instance.CreateAllocationAsync(RelayMaxConnections, targetRegion);
return HostStatus.Allocating;
}
// connectionType also supports udp, but this is not recommended
static RelayServerData HostRelayData(Allocation allocation, string connectionType = "dtls")
{

Просмотреть файл

@ -31,12 +31,16 @@ namespace Samples.HelloNetcode
{
var settings = DefaultDriverBuilder.GetNetworkSettings();
settings.WithRelayParameters(ref m_RelayClientData);
DefaultDriverBuilder.RegisterClientUdpDriver(world, ref driverStore, netDebug, settings);
DefaultDriverBuilder.RegisterClientDriver(world, ref driverStore, netDebug, settings);
}
public void CreateServerDriver(World world, ref NetworkDriverStore driverStore, NetDebug netDebug)
{
#if !UNITY_WEBGL || UNITY_EDITOR
DefaultDriverBuilder.RegisterServerDriver(world, ref driverStore, netDebug, ref m_RelayServerData);
#else
throw new System.NotSupportedException("It is not allowed to create a server NetworkDriver for WebGL build.");
#endif
}
}
}

Просмотреть файл

@ -1,8 +1,8 @@
#if !UNITY_SERVER
using Unity.Entities;
using Unity.NetCode;
using Unity.Networking.Transport;
using Unity.Networking.Transport.Relay;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
@ -18,7 +18,12 @@ namespace Samples.HelloNetcode
/// A bootstrap world is constructed to run the jobs for setting up host and client configuration for relay server.
/// Once this is done the game can be launched and the configuration can be retrieved from the constructed world.
/// </summary>
public class RelayFrontend : Frontend
public class RelayFrontend :
#if UNITY_SERVER
MonoBehaviour
#else
Frontend
#endif
{
public string HostConnectionStatus
{
@ -49,6 +54,7 @@ namespace Samples.HelloNetcode
JoinLocalGame,
}
#if !UNITY_SERVER
public void OnRelayEnable(Toggle value)
{
TogglePersistentState(!value.isOn);
@ -251,6 +257,6 @@ namespace Samples.HelloNetcode
// For a locally hosted server, the client would need to connect to NetworkEndpoint.AnyIpv4, and the relayClientData.Endpoint in all other cases.
client.EntityManager.SetComponentData(networkStreamEntity, new NetworkStreamRequestConnect { Endpoint = relayClientData.Endpoint });
}
#endif
}
}
#endif

Просмотреть файл

@ -28,9 +28,9 @@ RectTransform:
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7958460717334784095}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
@ -67,10 +67,10 @@ MonoBehaviour:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 14
m_FontSize: 28
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MinSize: 0
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
@ -108,15 +108,15 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7958460717230692451}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -364.76, y: 166}
m_SizeDelta: {x: 100, y: 30}
m_SizeDelta: {x: 300, y: 90}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7958460717334784088
CanvasRenderer:

Просмотреть файл

@ -25,3 +25,5 @@ To set up a disconnect timeout on the driver a custom/manual driver needs to be
Try it with one thin client added in the playermode tools to see what messages are passed around when it is disconnected. Try making a standalone build with this sample (can be picked from frontend sample list) and then terminate the standalone process when testing to see the timeout event.
The UI is not set up to do anything fancy but just demonstrate connection events on disconnections.
Please take note of the descriptions beneath the buttons labeled `ServerWorld`, `ClientWorld`, and `ThinClientWorld`. These descriptions indicate the specific world in which the given connection is stored (the name of the connection is the number which we can disconnect). For instance, if we are testing this sample in the editor with a single ThinClient, you will observe this connection appearing twice: once in the `ThinClientWorld` and once in the `ServerWorld`. This duplication occurs because both of these worlds store the same connection. In another scenario, if the server is operational in the editor and we connect to it from a different build, you will notice that there is a connection in the editor for the `ServerWorld` and in the build for the `ClientWorld`.

Просмотреть файл

@ -38,7 +38,7 @@ RenderSettings:
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1}
m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
@ -104,7 +104,7 @@ NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
@ -117,7 +117,7 @@ NavMeshSettings:
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
@ -163,12 +163,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 225538412}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 417.63858, y: 192.56496, z: -5.0249343}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &420232002
GameObject:
@ -182,6 +183,7 @@ GameObject:
- component: {fileID: 420232005}
- component: {fileID: 420232004}
- component: {fileID: 420232003}
- component: {fileID: 420232007}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
@ -218,11 +220,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_UiScaleMode: 1
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_ReferenceResolution: {x: 1920, y: 1080}
m_ScreenMatchMode: 1
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
@ -246,7 +248,9 @@ Canvas:
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
@ -260,15 +264,39 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!114 &420232007
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 420232002}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 0
m_StartCorner: 0
m_StartAxis: 0
m_CellSize: {x: 250, y: 150}
m_Spacing: {x: 15, y: 15}
m_Constraint: 0
m_ConstraintCount: 2
--- !u!1 &848706481
GameObject:
m_ObjectHideFlags: 0
@ -299,6 +327,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
m_Name:
m_EditorClassIdentifier:
m_SendPointerHoverToParent: 1
m_HorizontalAxis: Horizontal
m_VerticalAxis: Vertical
m_SubmitButton: Submit
@ -328,12 +357,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848706481}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1025617519
GameObject:
@ -345,6 +375,7 @@ GameObject:
m_Component:
- component: {fileID: 1025617521}
- component: {fileID: 1025617520}
- component: {fileID: 1025617522}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
@ -421,13 +452,37 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1025617519}
serializedVersion: 2
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!114 &1025617522
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1025617519}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Version: 3
m_UsePipelineSettings: 1
m_AdditionalLightsShadowResolutionTier: 2
m_LightLayerMask: 1
m_RenderingLayers: 1
m_CustomShadowLayers: 0
m_ShadowLayerMask: 1
m_ShadowRenderingLayers: 1
m_LightCookieSize: {x: 1, y: 1}
m_LightCookieOffset: {x: 0, y: 0}
m_SoftShadowQuality: 0
--- !u!1 &1739351329
GameObject:
m_ObjectHideFlags: 0
@ -468,9 +523,17 @@ Camera:
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
@ -504,12 +567,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1739351329}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2020952715
GameObject:
@ -556,10 +620,21 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2020952715}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 1739351332}
- {fileID: 1025617521}
- {fileID: 848706484}
- {fileID: 2020952717}
- {fileID: 420232006}
- {fileID: 225538414}

Просмотреть файл

@ -11,7 +11,7 @@ using UnityEngine.UI;
namespace Samples.HelloNetcode
{
[UpdateInGroup(typeof(HelloNetcodeSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ServerSimulation)]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ServerSimulation | WorldSystemFilterFlags.ThinClientSimulation)]
public partial class ConnectionMonitorSystem : SystemBase
{
private BeginSimulationEntityCommandBufferSystem m_CommandBufferSystem;
@ -35,7 +35,7 @@ namespace Samples.HelloNetcode
protected override void OnUpdate()
{
if (m_ConnectionUI == null)
m_ConnectionUI = GameObject.FindObjectOfType<ConnectionUI>();
m_ConnectionUI = GameObject.FindFirstObjectByType<ConnectionUI>();
var buffer = m_CommandBufferSystem.CreateCommandBuffer();
Entities.WithName("AddConnectionStateToNewConnections").WithNone<ConnectionState>().ForEach((Entity entity,
@ -45,6 +45,7 @@ namespace Samples.HelloNetcode
}).Run();
FixedString32Bytes worldName = World.Name;
var unmanagedWorld = World.Unmanaged;
// Buttons are laid out in columns according to worlds, Server,ClientWorld0,ClientWorld1 and so on
int worldIndex = 0;
if (int.TryParse(World.Name[World.Name.Length - 1].ToString(), out worldIndex))
@ -55,12 +56,15 @@ namespace Samples.HelloNetcode
UnityEngine.Debug.Log($"[{worldName}] New connection ID:{id.Value}");
// Not thread safe, so all UI logic is kept on main thread
ConnectionMonitorUIData.Connections.Data.Enqueue(new Connection(){Id = id.Value, WorldIndex = worldIndex, WorldName = worldName});
ConnectionMonitorUIData.Connections.Data.Enqueue(new Connection(){Id = id.Value, WorldIndex = worldIndex, World = unmanagedWorld});
}).Run();
Entities.WithName("HandleDisconnect").WithNone<NetworkStreamConnection>().ForEach((Entity entity, in ConnectionState state) =>
{
UnityEngine.Debug.Log($"[{worldName}] Connection disconnected ID:{state.NetworkId} Reason:{DisconnectReasonEnumToString.Convert((int)state.DisconnectReason)}");
UnityEngine.Debug.Log($"[{worldName}] Connection disconnected ID:{state.NetworkId} Reason:{state.DisconnectReason.ToFixedString()}");
// Not thread safe, so all UI logic is kept on main thread
ConnectionMonitorUIData.Connections.Data.Enqueue(new Connection(){Id = state.NetworkId, WorldIndex = worldIndex, World = unmanagedWorld, ConnectionDeleted = true});
buffer.RemoveComponent<ConnectionState>(entity);
}).Run();
@ -86,6 +90,15 @@ namespace Samples.HelloNetcode
return settings;
}
private void CreateDriver(out NetworkDriver driver, NetworkSettings settings)
{
#if !UNITY_WEBGL
driver = NetworkDriver.Create(new UDPNetworkInterface(), settings);
#else
driver = NetworkDriver.Create(new WebSocketNetworkInterface(), settings);
#endif
}
public void CreateClientDriver(World world, ref NetworkDriverStore driverStore, NetDebug netDebug)
{
var driverInstance = new NetworkDriverStore.NetworkDriverInstance();
@ -95,17 +108,17 @@ namespace Samples.HelloNetcode
if (NetworkSimulatorSettings.Enabled)
{
NetworkSimulatorSettings.SetSimulatorSettings(ref settings);
driverInstance.driver = NetworkDriver.Create(new UDPNetworkInterface(), settings);
CreateDriver(out driverInstance.driver, settings);
DefaultDriverBuilder.CreateClientSimulatorPipelines(ref driverInstance);
}
else
{
driverInstance.driver = NetworkDriver.Create(new UDPNetworkInterface(), settings);
CreateDriver(out driverInstance.driver, settings);
DefaultDriverBuilder.CreateClientPipelines(ref driverInstance);
}
#else
var settings = CreateNetworkSettings();
driverInstance.driver = NetworkDriver.Create(new UDPNetworkInterface(), settings);
CreateDriver(out driverInstance.driver, settings);
DefaultDriverBuilder.CreateClientPipelines(ref driverInstance);
#endif
driverStore.RegisterDriver(TransportType.Socket, driverInstance);
@ -113,11 +126,15 @@ namespace Samples.HelloNetcode
public void CreateServerDriver(World world, ref NetworkDriverStore driverStore, NetDebug netDebug)
{
#if UNITY_EDITOR || !UNITY_WEBGL
var settings = CreateNetworkSettings();
var driverInstance = new NetworkDriverStore.NetworkDriverInstance();
driverInstance.driver = NetworkDriver.Create(new UDPNetworkInterface(), settings);
CreateDriver(out driverInstance.driver, settings);
DefaultDriverBuilder.CreateServerPipelines(ref driverInstance);
driverStore.RegisterDriver(TransportType.Socket, driverInstance);
#else
throw new System.NotSupportedException("It is not allowed to create a server NetworkDriver for WebGL build.");
#endif
}
}
@ -149,7 +166,8 @@ namespace Samples.HelloNetcode
{
public int Id;
public int WorldIndex;
public FixedString32Bytes WorldName;
public WorldUnmanaged World;
public bool ConnectionDeleted;
}
public abstract class ConnectionMonitorUIData

Просмотреть файл

@ -28,9 +28,9 @@ RectTransform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
@ -67,10 +67,10 @@ MonoBehaviour:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 14
m_FontSize: 23
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 1
m_AlignByGeometry: 0

Просмотреть файл

@ -21,40 +21,46 @@ namespace Samples.HelloNetcode
return;
while (ConnectionMonitorUIData.Connections.Data.TryDequeue(out var con))
{
var buttonGo = UnityEngine.GameObject.Instantiate(m_Button, m_Canvas.transform, false);
buttonGo.GetComponent<RectTransform>().anchoredPosition3D +=
new Vector3(con.WorldIndex * m_VerticalSpace, (con.Id - 1) * m_HorizontalSpace, 0);
buttonGo.name = $"{con.WorldIndex} {con.Id}";
buttonGo.GetComponentInChildren<Text>().text = $"Disconnect {con.Id}";
buttonGo.onClick.AddListener(() => Disconnect(con));
if (con.ConnectionDeleted)
{
GameObject buttonToDelete = GameObject.Find(con.Id.ToString());
if (buttonToDelete != null)
{
Destroy(buttonToDelete);
}
}
else
{
var parent = new GameObject(con.Id.ToString());
parent.AddComponent<VerticalLayoutGroup>();
parent.transform.SetParent(m_Canvas.transform);
var buttonGo = Instantiate(m_Button, parent.transform, false);
buttonGo.GetComponent<RectTransform>().anchoredPosition3D += new Vector3(con.WorldIndex * m_VerticalSpace, (con.Id - 1) * m_HorizontalSpace, 0);
buttonGo.name = $"{con.WorldIndex} {con.Id}";
buttonGo.GetComponentInChildren<Text>().text = $"Disconnect {con.Id}";
buttonGo.onClick.AddListener(() => Disconnect(con));
var textGo = UnityEngine.GameObject.Instantiate(m_Text, m_Canvas.transform, false);
textGo.GetComponent<RectTransform>().anchoredPosition3D +=
new Vector3(con.WorldIndex * m_VerticalSpace, 0, 0);
textGo.name = $"{con.WorldIndex} {con.Id}";
textGo.text = con.WorldName.Value;
var textGo = Instantiate(m_Text, parent.transform, false);
textGo.GetComponent<RectTransform>().anchoredPosition3D += new Vector3(con.WorldIndex * m_VerticalSpace, 0, 0);
textGo.name = $"{con.WorldIndex} {con.Id}";
textGo.text = con.World.Name.Value;
}
}
}
public void Disconnect(Connection con)
{
UnityEngine.Debug.Log($"[{con.WorldName}] Disconnecting {con.Id}");
foreach (var world in World.All)
{
if (world.Name == con.WorldName)
{
var connection = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkId>(),
ComponentType.ReadOnly<NetworkStreamConnection>());
var connectionIds = connection.ToComponentDataArray<NetworkId>(Allocator.Temp);
var connectionEntities = connection.ToEntityArray(Allocator.Temp);
for (int i = 0; i < connectionIds.Length; ++i)
{
if (connectionIds[i].Value == con.Id)
world.EntityManager.AddComponent<NetworkStreamRequestDisconnect>(connectionEntities[i]);
}
}
}
Debug.Log($"[{con.World.Name.Value}] Disconnecting {con.Id}");
var connection = con.World.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<NetworkId>(),
ComponentType.ReadOnly<NetworkStreamConnection>());
var connectionIds = connection.ToComponentDataArray<NetworkId>(Allocator.Temp);
var connectionEntities = connection.ToEntityArray(Allocator.Temp);
for (int i = 0; i < connectionIds.Length; ++i)
{
if (connectionIds[i].Value == con.Id)
con.World.EntityManager.AddComponent<NetworkStreamRequestDisconnect>(connectionEntities[i]);
}
}
}
}

Просмотреть файл

@ -17,26 +17,16 @@ namespace Samples.HelloNetcode
public ScrollRect m_ScrollRect;
public GameObject m_ChatWindowPrefab;
List<World> m_ClientWorlds = new List<World>();
private int m_CurrentUserSlot = 0;
private int m_UserSlotHorizontalSpace = -13;
private int m_OwnUser = -1;
void Start()
{
foreach (var world in World.All)
{
if (world.IsClient() && !world.IsThinClient())
m_ClientWorlds.Add(world);
}
}
void Update()
{
if (m_OwnUser == -1)
if (m_OwnUser == -1 && ClientServerBootstrap.ClientWorld != null)
{
// Non-thin client will always be first in the list
var connectionQuery = m_ClientWorlds[0].EntityManager
var connectionQuery = ClientServerBootstrap.ClientWorld.EntityManager
.CreateEntityQuery(ComponentType.ReadOnly<NetworkId>());
var connectionIds = connectionQuery.ToComponentDataArray<NetworkId>(Allocator.Temp);
if (connectionIds.Length > 0)
@ -76,8 +66,7 @@ namespace Samples.HelloNetcode
public void SendChatMessage()
{
if (m_ClientWorlds.Count > 0)
SendRPC(m_ClientWorlds[0], m_InputText.text);
SendRPC(ClientServerBootstrap.ClientWorld, m_InputText.text);
// Clear the input text as the message has been sent, and place the UI focus back on it
// so it's ready to accept the next message
m_InputText.text = "";

Просмотреть файл

@ -17,6 +17,7 @@ GameObject:
- component: {fileID: -8791223551774281384}
- component: {fileID: 933197558220950793}
- component: {fileID: 7756198234017677449}
- component: {fileID: -7326952838053417493}
m_Layer: 0
m_Name: Player
m_TagString: Untagged
@ -184,3 +185,15 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
ComponentOverrides: []
--- !u!114 &-7326952838053417493
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5094733018780473589}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6f0825796d5f408c90844cba5ba8fdcc, type: 3}
m_Name:
m_EditorClassIdentifier:

Просмотреть файл

@ -0,0 +1,133 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: PrespawnBarrel
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.8113208, g: 0.8113208, b: 0.8113208, a: 1}
- _Color: {r: 0.8113208, g: 0.8113208, b: 0.8113208, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
--- !u!114 &8866395362223680394
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

Просмотреть файл

@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 83ca9d2de1b6dbc4db6df8360559b009
guid: ecd6118a9f570c84aba37745783b8564
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -65,7 +65,7 @@ MeshRenderer:
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
- {fileID: 2100000, guid: ecd6118a9f570c84aba37745783b8564, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0

Просмотреть файл

@ -0,0 +1,133 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: PrespawnBarrelChild
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.26415092, g: 0.26415092, b: 0.26415092, a: 1}
- _Color: {r: 0.26415092, g: 0.26415092, b: 0.26415092, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
--- !u!114 &8866395362223680394
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

Просмотреть файл

@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 9d4a6471a69208a499b9f55b8dedb016
guid: a97404c336661364aa910059eb7f0d6c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -30,6 +30,7 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.42018604, y: -0.22284698, z: 0.57614875}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
@ -53,6 +54,7 @@ MeshRenderer:
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
@ -61,7 +63,7 @@ MeshRenderer:
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
- {fileID: 2100000, guid: a97404c336661364aa910059eb7f0d6c, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
@ -91,8 +93,17 @@ CapsuleCollider:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2142660659608484602}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5000001
m_Height: 2
m_Direction: 1

Просмотреть файл

@ -0,0 +1,133 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: PrespawnBarrelWithNestedChild
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.09433961, g: 0.09433961, b: 0.09433961, a: 1}
- _Color: {r: 0.09433961, g: 0.09433961, b: 0.09433961, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
--- !u!114 &8866395362223680394
MonoBehaviour:
m_ObjectHideFlags: 11
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

Просмотреть файл

@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 27ec74688746f5747923ac7d239c23a4
guid: 73fb4638e69fcaf4fb164fefbecf8f22
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -67,7 +67,7 @@ MeshRenderer:
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
- {fileID: 2100000, guid: 73fb4638e69fcaf4fb164fefbecf8f22, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше