mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
Compare commits
347 Commits
TEngine3.0
...
TEngine4.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7914660f8c | ||
![]() |
b00679a276 | ||
![]() |
cd9a10ccac | ||
![]() |
f11098d32e | ||
![]() |
db6e715fa6 | ||
![]() |
c2edf71ee4 | ||
![]() |
75af7c22c7 | ||
![]() |
2f9edf83bf | ||
![]() |
815095efaf | ||
![]() |
3101a64a48 | ||
![]() |
457e0f36a4 | ||
![]() |
0bd30def23 | ||
![]() |
d24db83c8b | ||
![]() |
18a2840777 | ||
![]() |
1d901514e1 | ||
![]() |
983279c56d | ||
![]() |
e9540c620a | ||
![]() |
39be280d88 | ||
![]() |
f9f9122027 | ||
![]() |
682a0bd786 | ||
![]() |
34c514cae8 | ||
![]() |
c707cc1a38 | ||
![]() |
f016fdd0a6 | ||
![]() |
4c0f5a77f9 | ||
![]() |
ae0d00424f | ||
![]() |
c8ecac5815 | ||
![]() |
4f8c1cdfdd | ||
![]() |
dff043e075 | ||
![]() |
e69dc47b8f | ||
![]() |
fc2ef0714c | ||
![]() |
d75c1c8c93 | ||
![]() |
83bea559e4 | ||
![]() |
26689639ed | ||
![]() |
bd6ac5f4da | ||
![]() |
70dcb03a3f | ||
![]() |
59b3649155 | ||
![]() |
2c025efe3b | ||
![]() |
7ae6ef94ba | ||
![]() |
d5bb64b314 | ||
![]() |
4c34858ce0 | ||
![]() |
5adb2f83e5 | ||
![]() |
f10a89180e | ||
![]() |
3e3314858e | ||
![]() |
da57ed845f | ||
![]() |
df570f453a | ||
![]() |
a500a08a30 | ||
![]() |
2daac0e065 | ||
![]() |
514da6e99e | ||
![]() |
7f91241df7 | ||
![]() |
071974ab4f | ||
![]() |
c01e03ff70 | ||
![]() |
6fec792e05 | ||
![]() |
04ecf71eab | ||
![]() |
693ca5bb3d | ||
![]() |
90f84a2764 | ||
![]() |
6107b41770 | ||
![]() |
1423a3716a | ||
![]() |
6a87db76ee | ||
![]() |
3a6170dca6 | ||
![]() |
9d137d613a | ||
![]() |
ba77ec6b45 | ||
![]() |
b661da68f2 | ||
![]() |
e1040110bb | ||
![]() |
2d53fa1687 | ||
![]() |
c4ef07f13e | ||
![]() |
1f2d99ddc9 | ||
![]() |
edf4925a7a | ||
![]() |
5d67238c8f | ||
![]() |
6cfd352482 | ||
![]() |
40373c473d | ||
![]() |
d799f9fdf0 | ||
![]() |
15735c3d2d | ||
![]() |
818a74f437 | ||
![]() |
f248757401 | ||
![]() |
6ada0e7de7 | ||
![]() |
7ea472f97e | ||
![]() |
fb8528ff52 | ||
![]() |
e3ac92ef46 | ||
![]() |
be6a19c26a | ||
![]() |
e7f0636f30 | ||
![]() |
fb38e96e9e | ||
![]() |
dd658c7e1d | ||
![]() |
4d7cb7641d | ||
![]() |
ae075b1fad | ||
![]() |
02827ce3b8 | ||
![]() |
1a0e3f91e0 | ||
![]() |
6d376b0e07 | ||
![]() |
cb73c9a9eb | ||
![]() |
7dda73a7ac | ||
![]() |
9bcb636ed7 | ||
![]() |
bd0cfc5577 | ||
![]() |
ff613e4130 | ||
![]() |
fea1ae2278 | ||
![]() |
866c440479 | ||
![]() |
48ff839d64 | ||
![]() |
69be3cfa23 | ||
![]() |
5f2c27ecf0 | ||
![]() |
ef17cd851b | ||
![]() |
f186d6b058 | ||
![]() |
4385123976 | ||
![]() |
1334dc30f9 | ||
![]() |
af822add2c | ||
![]() |
ffb1f214ad | ||
![]() |
213aaed426 | ||
![]() |
623d301e41 | ||
![]() |
2870383afe | ||
![]() |
1ad435958a | ||
![]() |
386787c6ec | ||
![]() |
cd65dde4c3 | ||
![]() |
8321e77421 | ||
![]() |
1b6f80952e | ||
![]() |
b52e655c30 | ||
![]() |
8c0df95626 | ||
![]() |
0d1e308f1c | ||
![]() |
f8797538fd | ||
![]() |
fe4e168041 | ||
![]() |
8e9047d3a3 | ||
![]() |
04bfaeccc8 | ||
![]() |
d66c823c15 | ||
![]() |
0ec1424f0a | ||
![]() |
01f8eb9d57 | ||
![]() |
f5021a9688 | ||
![]() |
a632f7a5ad | ||
![]() |
5f968f4154 | ||
![]() |
c9fe83c2bd | ||
![]() |
2c00d103cb | ||
![]() |
1d56437d9f | ||
![]() |
69db1ff977 | ||
![]() |
48887b1aee | ||
![]() |
381ea8bb8d | ||
![]() |
7401edac15 | ||
![]() |
b1c7f30be9 | ||
![]() |
cfaf82a623 | ||
![]() |
6992d12c6c | ||
![]() |
119d9683ad | ||
![]() |
9478868513 | ||
![]() |
6ed32082e1 | ||
![]() |
6ee515e8c5 | ||
![]() |
b839afa76a | ||
![]() |
d9605b348a | ||
![]() |
dfef83919c | ||
![]() |
f5f983f220 | ||
![]() |
d61b1206ee | ||
![]() |
3650ba1a8b | ||
![]() |
8f14a4d2cb | ||
![]() |
dc22e595c9 | ||
![]() |
0e70f7d446 | ||
![]() |
039569b2d4 | ||
![]() |
ea38004ba2 | ||
![]() |
cc97c0583a | ||
![]() |
887094a4b1 | ||
![]() |
0d09a7e73b | ||
![]() |
d8f8514f9d | ||
![]() |
f2f6b2422f | ||
![]() |
3a9cad9397 | ||
![]() |
5e70e7972e | ||
![]() |
8d2b4200d6 | ||
![]() |
b983e85416 | ||
![]() |
99d2afdbd7 | ||
![]() |
57ce836b3c | ||
![]() |
8dce78d6fb | ||
![]() |
6d41adffd9 | ||
![]() |
cb9129261b | ||
![]() |
89dd6214d4 | ||
![]() |
1aec76d64c | ||
![]() |
859f654f6d | ||
![]() |
391d690f9d | ||
![]() |
bb0b4104f9 | ||
![]() |
20d0ecd8da | ||
![]() |
8c3d6308b9 | ||
![]() |
4c8c37ffd8 | ||
![]() |
5f694c2bed | ||
![]() |
7ff74bb747 | ||
![]() |
a5de63397a | ||
![]() |
13cc62f3f1 | ||
![]() |
95dfac5294 | ||
![]() |
14e95107c9 | ||
![]() |
caf5b2b54e | ||
![]() |
285483034e | ||
![]() |
1cdd8b63b4 | ||
![]() |
4c748df7ac | ||
![]() |
e1229b5a4b | ||
![]() |
b937fb1a37 | ||
![]() |
87ab99b363 | ||
![]() |
e3a47393f4 | ||
![]() |
75725314ad | ||
![]() |
d3ed2b21b9 | ||
![]() |
ec34dfbb16 | ||
![]() |
bd76e3a651 | ||
![]() |
278c8f23be | ||
![]() |
42568db2ab | ||
![]() |
b033c59b00 | ||
![]() |
04a43a3f11 | ||
![]() |
54214cdd0b | ||
![]() |
fa870b6228 | ||
![]() |
8a49d3437b | ||
![]() |
f19b889deb | ||
![]() |
d55f6e29d9 | ||
![]() |
8e4af31f26 | ||
![]() |
58a4b3e043 | ||
![]() |
576bf3bb48 | ||
![]() |
d282b81546 | ||
![]() |
d6dcd8851c | ||
![]() |
bbea9c4cee | ||
![]() |
b1ccb1fd53 | ||
![]() |
0f2ad3c71d | ||
![]() |
0e6851e691 | ||
![]() |
75b9956261 | ||
![]() |
401c397fc8 | ||
![]() |
94b314e91f | ||
![]() |
a6573d9336 | ||
![]() |
abf5357f49 | ||
![]() |
aab353cca3 | ||
![]() |
e13071c4db | ||
![]() |
e071c20214 | ||
![]() |
bf73ce333b | ||
![]() |
d7a60002d4 | ||
![]() |
61f657322d | ||
![]() |
33223dc02e | ||
![]() |
0661c59877 | ||
![]() |
7c74e10857 | ||
![]() |
db935bfb5f | ||
![]() |
dc6b7bb21e | ||
![]() |
d32f3cb768 | ||
![]() |
a32ab30444 | ||
![]() |
aa0d40c758 | ||
![]() |
5239b89326 | ||
![]() |
53f0ecb6c1 | ||
![]() |
ea9447d0ea | ||
![]() |
0c6d59f53e | ||
![]() |
9986e22d18 | ||
![]() |
7deb049769 | ||
![]() |
fce8bd4d60 | ||
![]() |
7ec14c670f | ||
![]() |
efe3d2b25b | ||
![]() |
2107282368 | ||
![]() |
fb26ea2297 | ||
![]() |
9ac150425f | ||
![]() |
36d2c146b0 | ||
![]() |
774b73bbbf | ||
![]() |
32366eb127 | ||
![]() |
a843617e5f | ||
![]() |
46b139f7cf | ||
![]() |
e6ff1dec3f | ||
![]() |
4429732010 | ||
![]() |
60a5caebae | ||
![]() |
f7c95d8216 | ||
![]() |
98dcb80942 | ||
![]() |
30192d52cf | ||
![]() |
b7b2262d53 | ||
![]() |
d1c93f15d6 | ||
![]() |
1ac1ff7d56 | ||
![]() |
863788f303 | ||
![]() |
74790c7486 | ||
![]() |
1c223c8ad0 | ||
![]() |
e75b3a4e66 | ||
![]() |
91b0995911 | ||
![]() |
14c886ea8f | ||
![]() |
9babc0ba85 | ||
![]() |
f8056aef32 | ||
![]() |
846dc4d4bc | ||
![]() |
03ab7fb353 | ||
![]() |
3c11980e7c | ||
![]() |
c23aa0bd71 | ||
![]() |
df76d0b77a | ||
![]() |
35d2012546 | ||
![]() |
c96d20a89a | ||
![]() |
5787d0f9dc | ||
![]() |
8b35c8ca07 | ||
![]() |
e5456da482 | ||
![]() |
144ba9f222 | ||
![]() |
889fbdc8e1 | ||
![]() |
0d177e6868 | ||
![]() |
29135228be | ||
![]() |
c1a1de73cd | ||
![]() |
612e9b7eba | ||
![]() |
5ed6b8c378 | ||
![]() |
06dad5a68a | ||
![]() |
9e0462043c | ||
![]() |
c1178e284b | ||
![]() |
8e3dd138a6 | ||
![]() |
405253c507 | ||
![]() |
66ef50a9e0 | ||
![]() |
413f4dcda7 | ||
![]() |
75fdb4d7de | ||
![]() |
1ada1e4e33 | ||
![]() |
3848b6aaaa | ||
![]() |
2b33b405c0 | ||
![]() |
6766b930d7 | ||
![]() |
526baf45fa | ||
![]() |
a473971cfb | ||
![]() |
51effd2c97 | ||
![]() |
4205220b64 | ||
![]() |
e95c18ca67 | ||
![]() |
f5571716a2 | ||
![]() |
d61a8dfa4b | ||
![]() |
068ec709d0 | ||
![]() |
8b26b790ee | ||
![]() |
da35b4306b | ||
![]() |
ee2147e3d9 | ||
![]() |
a273e9d5f8 | ||
![]() |
31d4d6c0b8 | ||
![]() |
6a6f0591f1 | ||
![]() |
9cd0bac81e | ||
![]() |
4abe10eecf | ||
![]() |
bd10297dfa | ||
![]() |
7ce72b8aa8 | ||
![]() |
c6ecb48944 | ||
![]() |
c178f8bb75 | ||
![]() |
d4f160d284 | ||
![]() |
d87653a9fb | ||
![]() |
04a5a0e048 | ||
![]() |
c825f4d920 | ||
![]() |
b3e59b92d6 | ||
![]() |
37af620d71 | ||
![]() |
493172a925 | ||
![]() |
1382db8c61 | ||
![]() |
72b7149aa1 | ||
![]() |
0c51ae7bdd | ||
![]() |
79dc302f14 | ||
![]() |
95e4dd93a8 | ||
![]() |
2b310c00f2 | ||
![]() |
fbb26ea9ea | ||
![]() |
bff8f20af3 | ||
![]() |
0c7ce5f28c | ||
![]() |
31fc9abdf9 | ||
![]() |
a087f40e8a | ||
![]() |
7b2fc7ce9f | ||
![]() |
dc4bb8cc9c | ||
![]() |
0c8f3a5f92 | ||
![]() |
a69f53592e | ||
![]() |
8757e1f550 | ||
![]() |
336d4b2eb9 | ||
![]() |
e0be062006 | ||
![]() |
5e69129667 | ||
![]() |
4c39ab79ca | ||
![]() |
a710d1cb76 | ||
![]() |
8472839394 | ||
![]() |
4988c99d31 | ||
![]() |
4fa62b2d79 | ||
![]() |
f549514d7c | ||
![]() |
f588038848 | ||
![]() |
9e0e3ebd50 | ||
![]() |
b07ef836a9 | ||
![]() |
7a1d593195 | ||
![]() |
bc113c5c6e | ||
![]() |
753bbdfb82 |
122
.gitignore
vendored
122
.gitignore
vendored
@@ -1,119 +1,15 @@
|
||||
# This .gitignore file should be placed at the root of your Unity project directory
|
||||
#
|
||||
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
|
||||
#
|
||||
/[Ll]ibrary/
|
||||
/[Tt]emp/
|
||||
/[Oo]bj/
|
||||
/[Bb]uild/
|
||||
/[Bb]uilds/
|
||||
/[Ll]ogs/
|
||||
/[Mm]emoryCaptures/
|
||||
/EditorBuild/
|
||||
# /[Aa]ssets/TResources/DLL/
|
||||
/[Aa]ssets/StreamingAssets
|
||||
/BuildBundleInfo/
|
||||
[Aa]ssets/AATest/
|
||||
[Aa]ssets/AATest.meta
|
||||
|
||||
# Asset meta data should only be ignored when the corresponding asset is also ignored
|
||||
!/[Aa]ssets/**/*.meta
|
||||
|
||||
# Uncomment this line if you wish to ignore the asset store tools plugin
|
||||
# /[Aa]ssets/AssetStoreTools*
|
||||
|
||||
# Autogenerated Jetbrains Rider plugin
|
||||
[Aa]ssets/Plugins/Editor/JetBrains*
|
||||
|
||||
# Visual Studio cache directory
|
||||
.vs/
|
||||
|
||||
# Gradle cache directory
|
||||
.gradle/
|
||||
|
||||
# Autogenerated VS/MD/Consulo solution and project files
|
||||
ExportedObj/
|
||||
.consulo/
|
||||
*.csproj
|
||||
*.unityproj
|
||||
*.sln
|
||||
*.suo
|
||||
*.tmp
|
||||
*.user
|
||||
*.userprefs
|
||||
*.pidb
|
||||
*.booproj
|
||||
*.svd
|
||||
*.pdb
|
||||
*.mdb
|
||||
*.opendb
|
||||
*.VC.db
|
||||
|
||||
# Unity3D generated meta files
|
||||
*.pidb.meta
|
||||
*.pdb.meta
|
||||
*.mdb.meta
|
||||
|
||||
# Unity3D generated file on crash reports
|
||||
sysinfo.txt
|
||||
|
||||
# Builds
|
||||
*.apk
|
||||
|
||||
# Crashlytics generated file
|
||||
crashlytics-build.properties
|
||||
|
||||
# TEnginePersistentDataPath
|
||||
TEnginePersistentDataPath/
|
||||
|
||||
# Hotfix
|
||||
TEngineHotUpdate/bin
|
||||
TEngineHotUpdate/obj
|
||||
|
||||
#HybirdCLR(HuaTuo)
|
||||
/HybirdCLRData/
|
||||
[Hh]ybridCLRData/
|
||||
|
||||
|
||||
#AATemp
|
||||
[Aa]ssets/AATemp/
|
||||
[Aa]ssets/AATemp.meta
|
||||
|
||||
#Rider
|
||||
/.idea/
|
||||
|
||||
# ABConfig
|
||||
[Aa]ssets/BuildConfig/
|
||||
[Aa]ssets/BuildConfig.meta
|
||||
|
||||
[Aa]ssets/StreamingAssets/
|
||||
[Aa]ssets/StreamingAssets.meta
|
||||
Assets/HybridCLRBuildCache/AssetBundleOutput.meta
|
||||
Assets/HybridCLRBuildCache/AssetBundleOutput/StandaloneWindows.meta
|
||||
Assets/HybridCLRBuildCache.meta
|
||||
Assets/HybridCLRBuildCache/AssetBundleSourceData.meta
|
||||
Assets/HybridCLRBuildCache/AssetBundleSourceData/StandaloneWindows.meta
|
||||
|
||||
#Bundles
|
||||
Bundles/
|
||||
|
||||
#Sandbox
|
||||
Sandbox/
|
||||
UnityProject/UserSettings/Layouts/default-2021.dwlt
|
||||
UnityProject/UserSettings/Search.settings
|
||||
|
||||
#Luban
|
||||
Luban/.cache.meta
|
||||
Tools/Luban/
|
||||
Tools/Luban.ClientServer/
|
||||
Configs/.cache.meta
|
||||
GenerateDatas/
|
||||
|
||||
#HybridCLR
|
||||
Assets/HybridCLRData.meta
|
||||
UserSettings/Search.settings
|
||||
#FileServer
|
||||
Tools/FileServer/AssetRoot
|
||||
|
||||
#Unity UserSettings
|
||||
UserSettings/Search.index
|
||||
UserSettings/Layouts/default-2021.dwlt
|
||||
|
||||
#UnityOnlineServiceData
|
||||
Assets/UnityOnlineServiceData.meta
|
||||
Assets/UnityOnlineServiceData
|
||||
|
||||
.DS_Store
|
||||
UnityProject/ProjectSettings/CommonBurstAotSettings.json
|
||||
UnityProject/ProjectSettings/BurstAotSettings_StandaloneWindows.json
|
||||
|
@@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad23fce0bd282924fa4aedb4da24684f
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,69 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!687078895 &4343727234628468602
|
||||
SpriteAtlas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: UIRaw_Atlas_Common
|
||||
serializedVersion: 2
|
||||
m_EditorData:
|
||||
serializedVersion: 2
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
anisoLevel: 0
|
||||
compressionQuality: 0
|
||||
maxTextureSize: 0
|
||||
textureCompression: 0
|
||||
filterMode: 1
|
||||
generateMipMaps: 0
|
||||
readable: 0
|
||||
crunchedCompression: 0
|
||||
sRGB: 1
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
m_BuildTarget: iPhone
|
||||
m_MaxTextureSize: 2048
|
||||
m_ResizeAlgorithm: 0
|
||||
m_TextureFormat: 49
|
||||
m_TextureCompression: 1
|
||||
m_CompressionQuality: 100
|
||||
m_CrunchedCompression: 0
|
||||
m_AllowsAlphaSplitting: 0
|
||||
m_Overridden: 1
|
||||
m_AndroidETC2FallbackOverride: 0
|
||||
m_ForceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
packingSettings:
|
||||
serializedVersion: 2
|
||||
padding: 2
|
||||
blockOffset: 1
|
||||
allowAlphaSplitting: 0
|
||||
enableRotation: 1
|
||||
enableTightPacking: 0
|
||||
enableAlphaDilation: 0
|
||||
secondaryTextureSettings: {}
|
||||
variantMultiplier: 1
|
||||
packables:
|
||||
- {fileID: 21300000, guid: f9a06e163014f4f46b14f4499d3e7240, type: 3}
|
||||
- {fileID: 21300000, guid: 2761fc23b4aa7e34187ac5ffbc3fad9b, type: 3}
|
||||
- {fileID: 21300000, guid: d623a2b7e069a4c4592d3da48f476189, type: 3}
|
||||
- {fileID: 21300000, guid: 57e4117f4cd6ae54284898652e70d553, type: 3}
|
||||
bindAsDefault: 1
|
||||
isAtlasV2: 0
|
||||
cachedData: {fileID: 0}
|
||||
m_MasterAtlas: {fileID: 0}
|
||||
m_PackedSprites:
|
||||
- {fileID: 21300000, guid: 2761fc23b4aa7e34187ac5ffbc3fad9b, type: 3}
|
||||
- {fileID: 21300000, guid: f9a06e163014f4f46b14f4499d3e7240, type: 3}
|
||||
- {fileID: 21300000, guid: d623a2b7e069a4c4592d3da48f476189, type: 3}
|
||||
- {fileID: 21300000, guid: 57e4117f4cd6ae54284898652e70d553, type: 3}
|
||||
m_PackedSpriteNamesToIndex:
|
||||
- red_button
|
||||
- blue_button
|
||||
- white_background
|
||||
- white_button
|
||||
m_RenderDataMap: {}
|
||||
m_Tag: UIRaw_Atlas_Common
|
||||
m_IsVariant: 0
|
||||
m_IsPlaceholder: 0
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f69993ae3e14f924aa33b8747244c587
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22d1bcdd317c6f64daeb9bf9742495f2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,62 +0,0 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngine.Editor.Inspector
|
||||
{
|
||||
[CustomEditor(typeof(Network))]
|
||||
internal sealed class NetworkInspector : GameFrameworkInspector
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
if (!EditorApplication.isPlaying)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
Network t = (Network)target;
|
||||
|
||||
if (IsPrefabInHierarchy(t.gameObject))
|
||||
{
|
||||
EditorGUILayout.LabelField("Network Channel Count", t.NetworkChannelCount.ToString());
|
||||
|
||||
INetworkChannel[] networkChannels = t.GetAllNetworkChannels();
|
||||
foreach (INetworkChannel networkChannel in networkChannels)
|
||||
{
|
||||
DrawNetworkChannel(networkChannel);
|
||||
}
|
||||
}
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void DrawNetworkChannel(INetworkChannel networkChannel)
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
{
|
||||
EditorGUILayout.LabelField(networkChannel.Name, networkChannel.Connected ? "Connected" : "Disconnected");
|
||||
EditorGUILayout.LabelField("Service Type", networkChannel.ServiceType.ToString());
|
||||
EditorGUILayout.LabelField("Address Family", networkChannel.AddressFamily.ToString());
|
||||
EditorGUILayout.LabelField("Local Address", networkChannel.Connected ? networkChannel.Socket.LocalEndPoint.ToString() : "Unavailable");
|
||||
EditorGUILayout.LabelField("Remote Address", networkChannel.Connected ? networkChannel.Socket.RemoteEndPoint.ToString() : "Unavailable");
|
||||
EditorGUILayout.LabelField("Send Packet", Utility.Text.Format("{0} / {1}", networkChannel.SendPacketCount, networkChannel.SentPacketCount));
|
||||
EditorGUILayout.LabelField("Receive Packet", Utility.Text.Format("{0} / {1}", networkChannel.ReceivePacketCount, networkChannel.ReceivedPacketCount));
|
||||
EditorGUILayout.LabelField("Miss Heart Beat Count", networkChannel.MissHeartBeatCount.ToString());
|
||||
EditorGUILayout.LabelField("Heart Beat", Utility.Text.Format("{0:F2} / {1:F2}", networkChannel.HeartBeatElapseSeconds, networkChannel.HeartBeatInterval));
|
||||
EditorGUI.BeginDisabledGroup(!networkChannel.Connected);
|
||||
{
|
||||
if (GUILayout.Button("Disconnect"))
|
||||
{
|
||||
networkChannel.Close();
|
||||
}
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3301ba180cdc446bbdf823c860ae7a68
|
||||
timeCreated: 1682045195
|
@@ -1,489 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.U2D;
|
||||
using UnityEngine;
|
||||
using UnityEngine.U2D;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
/// <summary>
|
||||
/// 图集导入管线。
|
||||
/// </summary>
|
||||
public class SpritePostprocessor : AssetPostprocessor
|
||||
{
|
||||
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
foreach (var s in importedAssets)
|
||||
{
|
||||
EditorSpriteSaveInfo.OnImportSprite(s);
|
||||
}
|
||||
|
||||
foreach (var s in deletedAssets)
|
||||
{
|
||||
EditorSpriteSaveInfo.OnDeleteSprite(s);
|
||||
}
|
||||
|
||||
foreach (var s in movedFromAssetPaths)
|
||||
{
|
||||
EditorSpriteSaveInfo.OnDeleteSprite(s);
|
||||
}
|
||||
|
||||
foreach (var s in movedAssets)
|
||||
{
|
||||
EditorSpriteSaveInfo.OnImportSprite(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class EditorSpriteSaveInfo
|
||||
{
|
||||
private const string NormalAtlasDir = "Assets/Atlas";
|
||||
private const string UISpritePath = "Assets/AssetRaw/UIRaw";
|
||||
private const string UIAtlasPath = "Assets/AssetRaw/UIRaw/Atlas";
|
||||
private const string UIRawPath = "Assets/AssetRaw/UIRaw/UIRaw";
|
||||
private static List<string> m_dirtyAtlasList = new List<string>();
|
||||
private static Dictionary<string, List<string>> m_allASprites = new Dictionary<string, List<string>>();
|
||||
private static Dictionary<string, string> m_uiAtlasMap = new Dictionary<string, string>();
|
||||
private static bool m_inited = false;
|
||||
private static bool m_dirty = false;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (m_inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.update += CheckDirty;
|
||||
}
|
||||
|
||||
public static void CheckDirty()
|
||||
{
|
||||
if (m_dirty)
|
||||
{
|
||||
m_dirty = false;
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
float lastProgress = -1;
|
||||
for (int i = 0; i < m_dirtyAtlasList.Count; i++)
|
||||
{
|
||||
string atlasName = m_dirtyAtlasList[i];
|
||||
Debug.Log("更新图集 : " + atlasName);
|
||||
var curProgress = (float)i / m_dirtyAtlasList.Count;
|
||||
if (curProgress > lastProgress + 0.01f)
|
||||
{
|
||||
lastProgress = curProgress;
|
||||
var progressText = $"当前进度:{i}/{m_dirtyAtlasList.Count} {atlasName}";
|
||||
bool cancel = EditorUtility.DisplayCancelableProgressBar("刷新图集" + atlasName, progressText, curProgress);
|
||||
if (cancel)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isUI = atlasName.StartsWith("UIRaw");
|
||||
SaveAtlas(atlasName, isUI);
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
m_dirtyAtlasList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnImportSprite(string assetPath)
|
||||
{
|
||||
if (!assetPath.StartsWith(UISpritePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TextureImporter ti = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
||||
|
||||
if (ti != null)
|
||||
{
|
||||
var modify = false;
|
||||
|
||||
if (assetPath.StartsWith(UISpritePath))
|
||||
{
|
||||
if (ti.textureType != TextureImporterType.Sprite)
|
||||
{
|
||||
ti.textureType = TextureImporterType.Sprite;
|
||||
modify = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ti.spritePackingTag))
|
||||
{
|
||||
ti.spritePackingTag = string.Empty;
|
||||
modify = true;
|
||||
}
|
||||
|
||||
var setting = new TextureImporterSettings();
|
||||
ti.ReadTextureSettings(setting);
|
||||
if (setting.spriteGenerateFallbackPhysicsShape)
|
||||
{
|
||||
setting.spriteGenerateFallbackPhysicsShape = false;
|
||||
ti.SetTextureSettings(setting);
|
||||
modify = true;
|
||||
}
|
||||
|
||||
if (IsKeepRawImage(assetPath))
|
||||
{
|
||||
//调整android格式
|
||||
var andPlatformSettings = ti.GetPlatformTextureSettings("Android");
|
||||
if (!andPlatformSettings.overridden)
|
||||
{
|
||||
andPlatformSettings.overridden = true;
|
||||
modify = true;
|
||||
}
|
||||
|
||||
if (andPlatformSettings.format != TextureImporterFormat.ASTC_6x6)
|
||||
{
|
||||
andPlatformSettings.format = TextureImporterFormat.ASTC_6x6;
|
||||
ti.SetPlatformTextureSettings(andPlatformSettings);
|
||||
modify = true;
|
||||
}
|
||||
|
||||
//调整ios格式
|
||||
var iosPlatformSettings = ti.GetPlatformTextureSettings("iPhone");
|
||||
if (!iosPlatformSettings.overridden)
|
||||
{
|
||||
iosPlatformSettings.overridden = true;
|
||||
modify = true;
|
||||
}
|
||||
|
||||
if (iosPlatformSettings.format != TextureImporterFormat.ASTC_5x5)
|
||||
{
|
||||
iosPlatformSettings.format = TextureImporterFormat.ASTC_5x5;
|
||||
iosPlatformSettings.compressionQuality = 50;
|
||||
ti.SetPlatformTextureSettings(iosPlatformSettings);
|
||||
modify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modify)
|
||||
{
|
||||
ti.SaveAndReimport();
|
||||
}
|
||||
|
||||
if (ti.textureType == TextureImporterType.Sprite)
|
||||
{
|
||||
OnProcessSprite(assetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否保持散图(不打图集)
|
||||
/// </summary>
|
||||
/// <param name="dirPath"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsKeepRawImage(string dirPath)
|
||||
{
|
||||
return dirPath.Contains("UIRaw/Raw/") || dirPath.Contains("UIRaw_Raw_");
|
||||
}
|
||||
|
||||
public static string GetSpritePath(string assetPath)
|
||||
{
|
||||
string path = assetPath.Substring(0, assetPath.LastIndexOf("."));
|
||||
path = path.Replace("Assets/AssetRaw/", "");
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据文件路径,返回图集名称
|
||||
/// </summary>
|
||||
/// <param name="fullName"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetPackageTag(string fullName)
|
||||
{
|
||||
fullName = fullName.Replace("\\", "/");
|
||||
int idx = fullName.LastIndexOf("UIRaw", StringComparison.Ordinal);
|
||||
if (idx == -1)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (IsKeepRawImage(fullName))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var atlasPath = fullName.Substring(idx);
|
||||
string str = atlasPath;
|
||||
str = str.Substring(0, str.LastIndexOf("/", StringComparison.Ordinal)).Replace("/", "_");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static void OnProcessSprite(string assetPath)
|
||||
{
|
||||
if (!assetPath.StartsWith("Assets"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (assetPath.StartsWith("Assets/UIRaw_Delete"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Init();
|
||||
|
||||
var spriteName = Path.GetFileNameWithoutExtension(assetPath);
|
||||
var spritePath = GetSpritePath(assetPath);
|
||||
if (!m_uiAtlasMap.TryGetValue(spriteName, out string oldAssetPath) || spritePath == oldAssetPath)
|
||||
{
|
||||
m_uiAtlasMap[spriteName] = spritePath;
|
||||
m_dirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"有重名的图片:{spriteName}\n旧图集:{oldAssetPath}\n新图集:{spritePath} ");
|
||||
m_uiAtlasMap[spriteName] = spritePath;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
string atlasName = GetPackageTag(assetPath);
|
||||
if (string.IsNullOrEmpty(atlasName))
|
||||
{
|
||||
bool keepRaw = IsKeepRawImage(assetPath);
|
||||
if (!keepRaw)
|
||||
{
|
||||
Debug.LogError($"empty packingTag of asset :{assetPath} !!!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> ret;
|
||||
if (!m_allASprites.TryGetValue(atlasName, out ret))
|
||||
{
|
||||
ret = new List<string>();
|
||||
m_allASprites.Add(atlasName, ret);
|
||||
}
|
||||
|
||||
if (!ret.Contains(assetPath))
|
||||
{
|
||||
ret.Add(assetPath);
|
||||
m_dirty = true;
|
||||
if (!m_dirtyAtlasList.Contains(atlasName))
|
||||
{
|
||||
m_dirtyAtlasList.Add(atlasName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnDeleteSprite(string assetPath)
|
||||
{
|
||||
if (assetPath.StartsWith("Assets/UIRaw_Delete"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!assetPath.StartsWith(UISpritePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Init();
|
||||
string atlasName = GetPackageTag(assetPath);
|
||||
if (!m_allASprites.TryGetValue(atlasName, out var ret))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//改成文件名的匹配
|
||||
if (!ret.Exists(s => Path.GetFileName(s) == Path.GetFileName(assetPath)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (assetPath.StartsWith(UISpritePath))
|
||||
{
|
||||
var spriteName = Path.GetFileNameWithoutExtension(assetPath);
|
||||
if (m_uiAtlasMap.ContainsKey(spriteName))
|
||||
{
|
||||
m_uiAtlasMap.Remove(spriteName);
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
ret.Remove(assetPath);
|
||||
m_dirty = true;
|
||||
if (!m_dirtyAtlasList.Contains(atlasName))
|
||||
{
|
||||
m_dirtyAtlasList.Add(atlasName);
|
||||
}
|
||||
}
|
||||
|
||||
#region 更新图集
|
||||
|
||||
public static void SaveAtlas(string atlasName, bool isUI)
|
||||
{
|
||||
List<Object> spriteList = new List<Object>();
|
||||
if (m_allASprites.ContainsKey(atlasName))
|
||||
{
|
||||
var list = m_allASprites[atlasName];
|
||||
list.Sort(StringComparer.Ordinal);
|
||||
|
||||
foreach (var s in list)
|
||||
{
|
||||
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(s);
|
||||
if (sprite != null)
|
||||
{
|
||||
spriteList.Add(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var path = $"{NormalAtlasDir}/{atlasName}.spriteatlas";
|
||||
|
||||
if (spriteList.Count == 0)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var atlas = new SpriteAtlas();
|
||||
var setting = new SpriteAtlasPackingSettings
|
||||
{
|
||||
blockOffset = 1,
|
||||
padding = 2,
|
||||
enableRotation = true
|
||||
};
|
||||
|
||||
bool isOpaque = atlasName.Contains("Opaque");
|
||||
|
||||
var textureSetting = new SpriteAtlasTextureSettings
|
||||
{
|
||||
generateMipMaps = false,
|
||||
sRGB = true,
|
||||
filterMode = FilterMode.Bilinear
|
||||
};
|
||||
atlas.SetTextureSettings(textureSetting);
|
||||
|
||||
var iphonePlatformSetting = atlas.GetPlatformSettings("iPhone");
|
||||
if (!iphonePlatformSetting.overridden)
|
||||
{
|
||||
iphonePlatformSetting.overridden = true;
|
||||
iphonePlatformSetting.format = isOpaque ? TextureImporterFormat.ASTC_5x5 : TextureImporterFormat.ASTC_5x5;
|
||||
iphonePlatformSetting.compressionQuality = 100;
|
||||
atlas.SetPlatformSettings(iphonePlatformSetting);
|
||||
}
|
||||
|
||||
var androidPlatformSetting = atlas.GetPlatformSettings("Android");
|
||||
if (isOpaque && !androidPlatformSetting.overridden)
|
||||
{
|
||||
androidPlatformSetting.overridden = true;
|
||||
androidPlatformSetting.format = TextureImporterFormat.ETC_RGB4;
|
||||
androidPlatformSetting.compressionQuality = 100;
|
||||
atlas.SetPlatformSettings(androidPlatformSetting);
|
||||
}
|
||||
|
||||
atlas.SetPackingSettings(setting);
|
||||
atlas.Add(spriteList.ToArray());
|
||||
|
||||
AssetDatabase.CreateAsset(atlas, path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 重新生成图集
|
||||
|
||||
private static Dictionary<string, List<string>> m_tempAllASprites = new Dictionary<string, List<string>>();
|
||||
|
||||
[MenuItem("TEngine/图集/重新生成UI图集")]
|
||||
static void ForceGenAtlas()
|
||||
{
|
||||
Init();
|
||||
List<string> needSaveAtlas = new List<string>();
|
||||
m_tempAllASprites.Clear();
|
||||
var findAssets = AssetDatabase.FindAssets("t:sprite", new[] { UIAtlasPath });
|
||||
foreach (var findAsset in findAssets)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(findAsset);
|
||||
var atlasName = GetPackageTag(path);
|
||||
if (!m_tempAllASprites.TryGetValue(atlasName, out var spriteList))
|
||||
{
|
||||
spriteList = new List<string>();
|
||||
m_tempAllASprites[atlasName] = spriteList;
|
||||
}
|
||||
|
||||
if (!spriteList.Contains(path))
|
||||
{
|
||||
spriteList.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
//有变化的才刷
|
||||
var iter = m_tempAllASprites.GetEnumerator();
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
bool needSave = false;
|
||||
var atlasName = iter.Current.Key;
|
||||
var newSpritesList = iter.Current.Value;
|
||||
|
||||
List<string> existSprites;
|
||||
if (m_allASprites.TryGetValue(atlasName, out existSprites))
|
||||
{
|
||||
if (existSprites.Count != newSpritesList.Count)
|
||||
{
|
||||
needSave = true;
|
||||
existSprites.Clear();
|
||||
existSprites.AddRange(newSpritesList);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < newSpritesList.Count; i++)
|
||||
{
|
||||
if (!existSprites.Contains(newSpritesList[i]))
|
||||
{
|
||||
needSave = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needSave)
|
||||
{
|
||||
existSprites.Clear();
|
||||
existSprites.AddRange(newSpritesList);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
needSave = true;
|
||||
m_allASprites.Add(atlasName, new List<string>(newSpritesList));
|
||||
}
|
||||
|
||||
if (needSave && !needSaveAtlas.Contains(atlasName))
|
||||
{
|
||||
needSaveAtlas.Add(atlasName);
|
||||
}
|
||||
}
|
||||
iter.Dispose();
|
||||
foreach (var atlas in needSaveAtlas)
|
||||
{
|
||||
Debug.LogFormat("Gen atlas:{0}", atlas);
|
||||
SaveAtlas(atlas, true);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
SpriteAtlasUtility.PackAllAtlases(EditorUserBuildSettings.activeBuildTarget);
|
||||
Debug.Log("Gen end");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6b7bfca93e005f45999c16054d999e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "BattleCore.Runtime",
|
||||
"rootNamespace": "BattleCore.Runtime",
|
||||
"references": [
|
||||
"GUID:aa06d4cc755c979489c256c1bcca1dfb",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:a90b2d3377c5e4a4db95cc44fb82045e"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 641632c4f8079b94f963b5284d859a12
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 890abd3a957e406ab422fc468ba6c169
|
||||
timeCreated: 1682353243
|
@@ -1,6 +0,0 @@
|
||||
namespace BattleCore.Runtime
|
||||
{
|
||||
public static class EntityExtension
|
||||
{
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4ef2dfa5ceac99458eb1745131a9c83
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,27 +0,0 @@
|
||||
using UnityEngine;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace BattleCore.Runtime
|
||||
{
|
||||
public static class MathematicsExt
|
||||
{
|
||||
public static int2 ToInt2(this Vector2Int vec) => new int2(vec.x, vec.y);
|
||||
public static int3 ToInt3(this Vector3Int vec) => new int3(vec.x, vec.y, vec.z);
|
||||
public static float2 ToFloat2(this Vector2 vec) => new float2(vec.x, vec.y);
|
||||
public static float3 ToFloat3(this Vector3 vec) => new float3(vec.x, vec.y, vec.z);
|
||||
|
||||
public static bool IsEquals(this int2 a, int2 b) => math.all(a == b);
|
||||
public static bool IsEquals(this int3 a, int3 b) => math.all(a == b);
|
||||
|
||||
|
||||
public static Vector2Int ToVec2(this int2 vec) => new Vector2Int(vec.x, vec.y);
|
||||
public static Vector3Int ToVec3(this int2 vec) => new Vector3Int(vec.x, vec.y, 0);
|
||||
public static Vector3Int ToVec3(this int3 vec) => new Vector3Int(vec.x, vec.y, vec.z);
|
||||
public static Vector2 ToVec2(this float2 vec) => new Vector2(vec.x, vec.y);
|
||||
public static Vector3 ToVec3(this float3 vec) => new Vector3(vec.x, vec.y, vec.z);
|
||||
public static int ManhattanDist(this int2 vec) => vec.x + vec.y;
|
||||
public static int ManhattanDist(this int3 vec) => vec.x + vec.y + vec.z;
|
||||
public static float ManhattanDist(this float2 vec) => vec.x + vec.y;
|
||||
public static float ManhattanDist(this float3 vec) => vec.x + vec.y + vec.z;
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b0e288b918a41698571ec3d36059851
|
||||
timeCreated: 1682353251
|
@@ -1,21 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BattleCore.Runtime
|
||||
{
|
||||
public class Empty : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fc394f8c6ad5304a82ff84b4263756f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
#region Attribute
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class UpdateAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class FixedUpdateAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class LateUpdateAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RoleLoginAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RoleLogoutAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80791cccf63d44faa348884ea0c506a2
|
||||
timeCreated: 1684553027
|
@@ -1,16 +0,0 @@
|
||||
namespace GameBase
|
||||
{
|
||||
public class BaseClsTemplate<T>
|
||||
{
|
||||
protected static T Imp;
|
||||
|
||||
/// <summary>
|
||||
/// Unity工程,注册处理函数。
|
||||
/// </summary>
|
||||
/// <param name="imp">实现类。</param>
|
||||
public static void RegisterImp(T imp)
|
||||
{
|
||||
Imp = imp;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb7ef2f42b33ada47b3543dc7723fcb5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,263 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 通过LogicSys来驱动且具备Unity完整生命周期的单例(不继承MonoBehaviour)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BehaviourSingleton<T> : BaseBehaviourSingleton where T : BaseBehaviourSingleton, new()
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _instance)
|
||||
{
|
||||
_instance = new T();
|
||||
Log.Assert(_instance != null);
|
||||
_instance.Awake();
|
||||
RegSingleton(_instance);
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegSingleton(BaseBehaviourSingleton inst)
|
||||
{
|
||||
BehaviourSingleSystem.Instance.RegSingleton(inst);
|
||||
}
|
||||
}
|
||||
|
||||
public class BaseBehaviourSingleton
|
||||
{
|
||||
public bool IsStart = false;
|
||||
|
||||
public virtual void Active()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool IsHaveLateUpdate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 帧更新。
|
||||
/// <remarks>需要UpdateAttribute。</remarks>
|
||||
/// </summary>
|
||||
public virtual void Update()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后帧更新。
|
||||
/// <remarks>需要LateUpdateAttribute。</remarks>
|
||||
/// </summary>
|
||||
public virtual void LateUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物理帧更新。
|
||||
/// <remarks>需要FixedUpdateAttribute。</remarks>
|
||||
/// </summary>
|
||||
public virtual void FixedUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Destroy()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnPause()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnResume()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnDrawGizmos()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class BehaviourSingleSystem : BaseLogicSys<BehaviourSingleSystem>
|
||||
{
|
||||
private readonly List<BaseBehaviourSingleton> _listInst = new List<BaseBehaviourSingleton>();
|
||||
private readonly List<BaseBehaviourSingleton> _listStart = new List<BaseBehaviourSingleton>();
|
||||
private readonly List<BaseBehaviourSingleton> _listUpdate = new List<BaseBehaviourSingleton>();
|
||||
private readonly List<BaseBehaviourSingleton> _listLateUpdate = new List<BaseBehaviourSingleton>();
|
||||
private readonly List<BaseBehaviourSingleton> _listFixedUpdate = new List<BaseBehaviourSingleton>();
|
||||
|
||||
public void RegSingleton(BaseBehaviourSingleton inst)
|
||||
{
|
||||
Log.Assert(!_listInst.Contains(inst));
|
||||
_listInst.Add(inst);
|
||||
_listStart.Add(inst);
|
||||
if (HadAttribute<UpdateAttribute>(inst.GetType()))
|
||||
{
|
||||
_listUpdate.Add(inst);
|
||||
}
|
||||
if (HadAttribute<LateUpdateAttribute>(inst.GetType()))
|
||||
{
|
||||
_listLateUpdate.Add(inst);
|
||||
}
|
||||
if (HadAttribute<FixedUpdateAttribute>(inst.GetType()))
|
||||
{
|
||||
_listFixedUpdate.Add(inst);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnRegSingleton(BaseBehaviourSingleton inst)
|
||||
{
|
||||
if (inst == null)
|
||||
{
|
||||
Log.Error($"BaseBehaviourSingleton Is Null");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Assert(_listInst.Contains(inst));
|
||||
if (_listInst.Contains(inst))
|
||||
{
|
||||
_listInst.Remove(inst);
|
||||
}
|
||||
|
||||
if (_listStart.Contains(inst))
|
||||
{
|
||||
_listStart.Remove(inst);
|
||||
}
|
||||
|
||||
if (_listUpdate.Contains(inst))
|
||||
{
|
||||
_listUpdate.Remove(inst);
|
||||
}
|
||||
|
||||
if (_listLateUpdate.Contains(inst))
|
||||
{
|
||||
_listLateUpdate.Remove(inst);
|
||||
}
|
||||
|
||||
inst.Destroy();
|
||||
inst = null;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
var listStart = _listStart;
|
||||
var listToUpdate = _listUpdate;
|
||||
var listToLateUpdate = _listLateUpdate;
|
||||
if (listStart.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < listStart.Count; i++)
|
||||
{
|
||||
var inst = listStart[i];
|
||||
Log.Assert(!inst.IsStart);
|
||||
|
||||
inst.IsStart = true;
|
||||
inst.Start();
|
||||
listToUpdate.Add(inst);
|
||||
|
||||
if (inst.IsHaveLateUpdate())
|
||||
{
|
||||
listToLateUpdate.Add(inst);
|
||||
}
|
||||
}
|
||||
|
||||
listStart.Clear();
|
||||
}
|
||||
|
||||
var listUpdateCnt = listToUpdate.Count;
|
||||
for (int i = 0; i < listUpdateCnt; i++)
|
||||
{
|
||||
var inst = listToUpdate[i];
|
||||
|
||||
TProfiler.BeginFirstSample(inst.GetType().FullName);
|
||||
inst.Update();
|
||||
TProfiler.EndFirstSample();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLateUpdate()
|
||||
{
|
||||
var listLateUpdate = _listLateUpdate;
|
||||
var listLateUpdateCnt = listLateUpdate.Count;
|
||||
for (int i = 0; i < listLateUpdateCnt; i++)
|
||||
{
|
||||
var inst = listLateUpdate[i];
|
||||
|
||||
TProfiler.BeginFirstSample(inst.GetType().FullName);
|
||||
inst.LateUpdate();
|
||||
TProfiler.EndFirstSample();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate()
|
||||
{
|
||||
var listFixedUpdate = _listFixedUpdate;
|
||||
var listFixedUpdateCnt = listFixedUpdate.Count;
|
||||
for (int i = 0; i < listFixedUpdateCnt; i++)
|
||||
{
|
||||
var inst = listFixedUpdate[i];
|
||||
|
||||
TProfiler.BeginFirstSample(inst.GetType().FullName);
|
||||
inst.FixedUpdate();
|
||||
TProfiler.EndFirstSample();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
for (int i = 0; i < _listInst.Count; i++)
|
||||
{
|
||||
var inst = _listInst[i];
|
||||
inst.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnApplicationPause(bool pause)
|
||||
{
|
||||
for (int i = 0; i < _listInst.Count; i++)
|
||||
{
|
||||
var inst = _listInst[i];
|
||||
if (pause)
|
||||
{
|
||||
inst.OnPause();
|
||||
}
|
||||
else
|
||||
{
|
||||
inst.OnResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDrawGizmos()
|
||||
{
|
||||
for (int i = 0; i < _listInst.Count; i++)
|
||||
{
|
||||
var inst = _listInst[i];
|
||||
inst.OnDrawGizmos();
|
||||
}
|
||||
}
|
||||
|
||||
private bool HadAttribute<T>(Type type) where T:Attribute
|
||||
{
|
||||
T attribute = Attribute.GetCustomAttribute(type, typeof(T)) as T;
|
||||
|
||||
return attribute != null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b062b3e32edd4536a4308a3d180842e0
|
||||
timeCreated: 1681989133
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dab5f3e08b4367d41b801af29d381ca8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,190 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
|
||||
/*******************************************************************************
|
||||
//开启一个Loom进程
|
||||
Loom.RunAsync(() =>
|
||||
{
|
||||
aucThread = new Thread(ReceiveMsg);
|
||||
aucThread.Start();
|
||||
}
|
||||
|
||||
//进程调用主线程方法
|
||||
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, 0, len);
|
||||
Loom.QueueOnMainThread((param) =>
|
||||
{
|
||||
UdpHandleResponse(pack);
|
||||
}, null);
|
||||
|
||||
*******************************************************************************/
|
||||
namespace GameBase
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Loom多线程通信。
|
||||
/// <remarks></remarks>
|
||||
/// </summary>
|
||||
public class Loom : MonoBehaviour
|
||||
{
|
||||
public Dictionary<string, CancellationTokenSource> TokenSourcesDictionary = new Dictionary<string, CancellationTokenSource>();
|
||||
private static readonly int MaxThreads = 8;
|
||||
private static int _numThreads;
|
||||
private static Loom _current;
|
||||
|
||||
public static Loom Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Initialize();
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
_current = this;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected void OnDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
private static bool _initialized;
|
||||
|
||||
private static void Initialize()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
|
||||
var obj = new GameObject("[Loom]");
|
||||
|
||||
_current = obj.AddComponent<Loom>();
|
||||
|
||||
DontDestroyOnLoad(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public struct NoDelayedQueueItem
|
||||
{
|
||||
public Action<object> Action;
|
||||
public object Param;
|
||||
}
|
||||
|
||||
private readonly List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
|
||||
|
||||
public struct DelayedQueueItem
|
||||
{
|
||||
public float Time;
|
||||
public Action<object> Action;
|
||||
public object Param;
|
||||
}
|
||||
|
||||
private readonly List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
|
||||
|
||||
private readonly List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
|
||||
|
||||
public static void QueueOnMainThread(Action<object> taction, object param, float time = 0f)
|
||||
{
|
||||
if (time != 0f)
|
||||
{
|
||||
lock (Current._delayed)
|
||||
{
|
||||
Current._delayed.Add(new DelayedQueueItem { Time = Time.time + time, Action = taction, Param = param });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (Current._actions)
|
||||
{
|
||||
Current._actions.Add(new NoDelayedQueueItem { Action = taction, Param = param });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Thread RunAsync(Action action)
|
||||
{
|
||||
Initialize();
|
||||
while (_numThreads >= MaxThreads)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _numThreads);
|
||||
ThreadPool.QueueUserWorkItem(RunAction, action);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void RunAction(object action)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action)action)();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref _numThreads);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (_current == this)
|
||||
{
|
||||
_current = null;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>();
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_actions.Count > 0)
|
||||
{
|
||||
lock (_actions)
|
||||
{
|
||||
_currentActions.Clear();
|
||||
_currentActions.AddRange(_actions);
|
||||
_actions.Clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < _currentActions.Count; i++)
|
||||
{
|
||||
_currentActions[i].Action(_currentActions[i].Param);
|
||||
}
|
||||
}
|
||||
|
||||
if (_delayed.Count > 0)
|
||||
{
|
||||
lock (_delayed)
|
||||
{
|
||||
_currentDelayed.Clear();
|
||||
_currentDelayed.AddRange(_delayed.Where(d => d.Time <= Time.time));
|
||||
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||
{
|
||||
_delayed.Remove(_currentDelayed[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||
{
|
||||
_currentDelayed[i].Action(_currentDelayed[i].Param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eea4adc555e37f842abf5dd24c191d93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 464445719fff48a78021df4a4a6b05a5
|
||||
timeCreated: 1684552621
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00565f0d362f4c36836804455e19c3df
|
||||
timeCreated: 1681990210
|
@@ -1,27 +0,0 @@
|
||||
using TEngine;
|
||||
|
||||
namespace GameBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用单例。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">泛型T。</typeparam>
|
||||
public class Singleton<T> where T : new()
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _instance)
|
||||
{
|
||||
_instance = new T();
|
||||
Log.Assert(_instance != null);
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,214 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using TEngine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace GameBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 单例接口。
|
||||
/// </summary>
|
||||
public interface ISingleton
|
||||
{
|
||||
void Active();
|
||||
|
||||
void Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单例管理器(统一化持久和释放)。
|
||||
/// </summary>
|
||||
public static class SingletonMgr
|
||||
{
|
||||
private static List<ISingleton> _singletonList;
|
||||
private static Dictionary<string, GameObject> _gameObjects;
|
||||
private static GameObject _root;
|
||||
|
||||
public static GameObject Root
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_root == null)
|
||||
{
|
||||
_root = GameObject.Find("[GameModule]");
|
||||
|
||||
if (_root == null)
|
||||
{
|
||||
_root = new GameObject("[GameModule]")
|
||||
{
|
||||
transform =
|
||||
{
|
||||
position = Vector3.zero
|
||||
}
|
||||
};
|
||||
}
|
||||
Object.DontDestroyOnLoad(_root);
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Retain(ISingleton go)
|
||||
{
|
||||
if (_singletonList == null)
|
||||
{
|
||||
_singletonList = new List<ISingleton>();
|
||||
}
|
||||
|
||||
_singletonList.Add(go);
|
||||
}
|
||||
|
||||
public static void Retain(GameObject go)
|
||||
{
|
||||
if (_gameObjects == null)
|
||||
{
|
||||
_gameObjects = new Dictionary<string, GameObject>();
|
||||
}
|
||||
|
||||
if (!_gameObjects.ContainsKey(go.name))
|
||||
{
|
||||
_gameObjects.Add(go.name, go);
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Object.DontDestroyOnLoad(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Release(GameObject go)
|
||||
{
|
||||
if (_gameObjects != null && _gameObjects.ContainsKey(go.name))
|
||||
{
|
||||
_gameObjects.Remove(go.name);
|
||||
Object.Destroy(go);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Release(ISingleton go)
|
||||
{
|
||||
if (_singletonList != null && _singletonList.Contains(go))
|
||||
{
|
||||
_singletonList.Remove(go);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Release()
|
||||
{
|
||||
if (_gameObjects != null)
|
||||
{
|
||||
foreach (var item in _gameObjects)
|
||||
{
|
||||
UnityEngine.Object.Destroy(item.Value);
|
||||
}
|
||||
|
||||
_gameObjects.Clear();
|
||||
}
|
||||
|
||||
if (_singletonList != null)
|
||||
{
|
||||
for (int i = 0; i < _singletonList.Count; ++i)
|
||||
{
|
||||
_singletonList[i].Release();
|
||||
}
|
||||
|
||||
_singletonList.Clear();
|
||||
}
|
||||
|
||||
Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
public static GameObject GetGameObject(string name)
|
||||
{
|
||||
GameObject go = null;
|
||||
if (_gameObjects != null)
|
||||
{
|
||||
_gameObjects.TryGetValue(name, out go);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
internal static bool ContainsKey(string name)
|
||||
{
|
||||
if (_gameObjects != null)
|
||||
{
|
||||
return _gameObjects.ContainsKey(name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static ISingleton GetSingleton(string name)
|
||||
{
|
||||
for (int i = 0; i < _singletonList.Count; ++i)
|
||||
{
|
||||
if (_singletonList[i].ToString() == name)
|
||||
{
|
||||
return _singletonList[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放所有单例。
|
||||
/// </summary>
|
||||
public static void ReStart()
|
||||
{
|
||||
Release();
|
||||
|
||||
SceneManager.LoadScene(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局单例对象(非线程安全)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">泛型T。</typeparam>
|
||||
public abstract class TSingleton<T> : ISingleton where T : TSingleton<T>, new()
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _instance)
|
||||
{
|
||||
_instance = new T();
|
||||
_instance.Init();
|
||||
#if UNITY_EDITOR
|
||||
Log.Info($"TSingleton Instance:{typeof(T).Name}");
|
||||
#endif
|
||||
SingletonMgr.Retain(_instance);
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValid => _instance != null;
|
||||
|
||||
protected TSingleton()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void Init()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Active()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Release()
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
SingletonMgr.Release(_instance);
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa2cb5bb622045d7a6f2a4c4faea3ca6
|
||||
timeCreated: 1681989590
|
@@ -1,114 +0,0 @@
|
||||
using TEngine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 具备Unity完整生命周期的单例。
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class UnitySingleton<T> : MonoBehaviour where T : MonoBehaviour
|
||||
{
|
||||
private static T _instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
var ins = FindObjectOfType<T>();
|
||||
if (ins != null)
|
||||
{
|
||||
var obj = ins.gameObject;
|
||||
obj.name = typeof(T).Name;
|
||||
_instance = ins;
|
||||
SingletonMgr.Retain(obj);
|
||||
return Instance;
|
||||
}
|
||||
|
||||
System.Type thisType = typeof(T);
|
||||
string instName = thisType.Name;
|
||||
GameObject go = SingletonMgr.GetGameObject(instName);
|
||||
if (go == null)
|
||||
{
|
||||
go = GameObject.Find($"[{instName}]");
|
||||
if (go == null)
|
||||
{
|
||||
go = new GameObject($"[{instName}]")
|
||||
{
|
||||
transform =
|
||||
{
|
||||
position = Vector3.zero
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_instance = go.GetComponent<T>();
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = go.AddComponent<T>();
|
||||
}
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
Log.Error($"Can't create UnitySingleton<{typeof(T)}>");
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static T Active()
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public static bool IsValid => _instance != null;
|
||||
|
||||
private bool CheckInstance()
|
||||
{
|
||||
if (this == Instance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GameObject.Destroy(gameObject);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void OnLoad()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
if (CheckInstance())
|
||||
{
|
||||
OnLoad();
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
Log.Debug($"UnitySingleton Instance:{typeof(T).Name}");
|
||||
#endif
|
||||
GameObject tEngine = SingletonMgr.Root;
|
||||
if (tEngine != null)
|
||||
{
|
||||
this.gameObject.transform.SetParent(tEngine.transform);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
public static void Release()
|
||||
{
|
||||
if (_instance == null) return;
|
||||
SingletonMgr.Release(_instance.gameObject);
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25c99243aa534df5870e36fdf9d36afd
|
||||
timeCreated: 1681990223
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d862c80fdc1e684e8ff3b6ae7707b79
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,24 +0,0 @@
|
||||
using TEngine;
|
||||
using UnityEngine;
|
||||
#if ENABLE_URP
|
||||
using UnityEngine.Rendering.Universal;
|
||||
#endif
|
||||
|
||||
namespace GameLogic
|
||||
{
|
||||
public class CameraUtils
|
||||
{
|
||||
public static void AddCameraStack(Camera camera,Camera mainCamera)
|
||||
{
|
||||
#if ENABLE_URP
|
||||
if (mainCamera != null)
|
||||
{
|
||||
// 通过脚本的方式,只要能找到 camera 不轮是否跨 base 相机的场景,都可以 Add 进 Stack
|
||||
mainCamera.GetComponent<UniversalAdditionalCameraData>().cameraStack.Add(GameModule.UI.UICamera);
|
||||
}
|
||||
#else
|
||||
Log.Fatal("Could not add camera stack because had no URP-Render-Pip");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6375b5490bbdcc145a24706a6c4e9cb7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4703541a565f5ec4bb35edd81c28958c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "GameLogic",
|
||||
"rootNamespace": "GameLogic",
|
||||
"references": [
|
||||
"GUID:08c3762f54316454ca6b6fbcb22b40e5",
|
||||
"GUID:a90b2d3377c5e4a4db95cc44fb82045e",
|
||||
"GUID:aa06d4cc755c979489c256c1bcca1dfb",
|
||||
"GUID:641632c4f8079b94f963b5284d859a12",
|
||||
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||
"GUID:15fc0a57446b3144c949da3e2b9737a9"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.render-pipelines.universal",
|
||||
"expression": "",
|
||||
"define": "ENABLE_URP"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 145b951be40d41dea06e76bd967a5d15
|
||||
timeCreated: 1682045847
|
@@ -1,166 +0,0 @@
|
||||
using TEngine;
|
||||
|
||||
namespace GameLogic
|
||||
{
|
||||
public enum ClientConnectWatcherStatus
|
||||
{
|
||||
StatusInit,
|
||||
StatusReconnectAuto,
|
||||
StatusReconnectConfirm,
|
||||
StatusWaitExit
|
||||
}
|
||||
|
||||
public class ClientConnectWatcher
|
||||
{
|
||||
private readonly GameClient _client;
|
||||
private ClientConnectWatcherStatus _status;
|
||||
private float _statusTime;
|
||||
private int _reconnectCnt = 0;
|
||||
private int _disconnectReason = 0;
|
||||
|
||||
private bool _enable = false;
|
||||
|
||||
public bool Enable
|
||||
{
|
||||
get => _enable;
|
||||
set
|
||||
{
|
||||
if (_enable != value)
|
||||
{
|
||||
_enable = value;
|
||||
if (_enable)
|
||||
{
|
||||
OnEnable();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDisable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClientConnectWatcherStatus Status
|
||||
{
|
||||
get => _status;
|
||||
set
|
||||
{
|
||||
if (_status == value) return;
|
||||
_status = value;
|
||||
_statusTime = NowTime;
|
||||
}
|
||||
}
|
||||
|
||||
private float NowTime => GameTime.unscaledTime;
|
||||
|
||||
public ClientConnectWatcher(GameClient client)
|
||||
{
|
||||
_client = client;
|
||||
_statusTime = NowTime;
|
||||
_status = ClientConnectWatcherStatus.StatusInit;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!_enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_client.IsEntered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_status)
|
||||
{
|
||||
case ClientConnectWatcherStatus.StatusInit:
|
||||
UpdateOnInitStatus();
|
||||
break;
|
||||
case ClientConnectWatcherStatus.StatusReconnectAuto:
|
||||
UpdateOnReconnectAuto();
|
||||
break;
|
||||
case ClientConnectWatcherStatus.StatusReconnectConfirm:
|
||||
UpdateOnReconnectConfirm();
|
||||
break;
|
||||
case ClientConnectWatcherStatus.StatusWaitExit:
|
||||
UpdateOnWaitExit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReConnect()
|
||||
{
|
||||
if (_status == ClientConnectWatcherStatus.StatusReconnectConfirm)
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOnInitStatus()
|
||||
{
|
||||
int autoReconnectMaxCount = 4;
|
||||
if (_reconnectCnt <= autoReconnectMaxCount)
|
||||
{
|
||||
if (_reconnectCnt == 0)
|
||||
{
|
||||
_disconnectReason = _client.LastNetErrCode;
|
||||
}
|
||||
|
||||
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
||||
_reconnectCnt++;
|
||||
|
||||
//重连
|
||||
_client.Reconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusReconnectConfirm;
|
||||
_reconnectCnt++;
|
||||
// UISys.Mgr.ShowUI(GAME_UI_TYPE.Tip_NetDisconn, UISys.Mgr.GetUIWindowParam().SetParam("errCode", m_disconnectReason));
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOnReconnectAuto()
|
||||
{
|
||||
if (_client.IsEntered)
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
_reconnectCnt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
float nowTime = NowTime;
|
||||
var timeoutTime = 10f;
|
||||
|
||||
if (_statusTime + timeoutTime < nowTime)
|
||||
{
|
||||
Log.Error("UpdateOnReconnectAuto timeout: {0}", timeoutTime);
|
||||
|
||||
//切换到默认的,下一帧继续判断是否需要自动还是手动
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOnReconnectConfirm()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UpdateOnWaitExit()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
_reconnectCnt = 0;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
_reconnectCnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e4e637f3da340dd9512150c3e2ff087
|
||||
timeCreated: 1684334948
|
@@ -1,428 +0,0 @@
|
||||
using System.Net.Sockets;
|
||||
using GameBase;
|
||||
using GameProto;
|
||||
using TEngine;
|
||||
using CSPkg = GameProto.CSPkg;
|
||||
|
||||
namespace GameLogic
|
||||
{
|
||||
public enum GameClientStatus
|
||||
{
|
||||
StatusInit, //初始化
|
||||
StatusReconnect, //重新连接
|
||||
StatusClose, //断开连接
|
||||
StatusLogin, //登录中
|
||||
StatusEnter, //AccountLogin成功,进入服务器了
|
||||
}
|
||||
|
||||
public enum CsMsgResult
|
||||
{
|
||||
NoError = 0,
|
||||
NetworkError = 1,
|
||||
InternalError = 2,
|
||||
MsgTimeOut = 3,
|
||||
PingTimeOut = 4,
|
||||
}
|
||||
|
||||
//定义消息回报的回调接口
|
||||
public delegate void CsMsgDelegate(CsMsgResult result, CSPkg msg);
|
||||
|
||||
/// <summary>
|
||||
/// 统计网络协议的接口
|
||||
/// </summary>
|
||||
public delegate void CsMsgStatDelegate(int cmdID, int pkgSize);
|
||||
|
||||
public class GameClient : Singleton<GameClient>
|
||||
{
|
||||
private readonly INetworkChannel _channel;
|
||||
|
||||
private GameClientStatus _status = GameClientStatus.StatusInit;
|
||||
|
||||
private readonly MsgDispatcher _dispatcher;
|
||||
|
||||
private readonly ClientConnectWatcher _connectWatcher;
|
||||
|
||||
private float _lastLogDisconnectErrTime = 0f;
|
||||
|
||||
private int _lastNetErrCode = 0;
|
||||
|
||||
public int LastNetErrCode => _lastNetErrCode;
|
||||
|
||||
public GameClientStatus Status
|
||||
{
|
||||
get => _status;
|
||||
set => _status = value;
|
||||
}
|
||||
|
||||
public bool IsEntered => _status == GameClientStatus.StatusEnter;
|
||||
|
||||
/// <summary>
|
||||
/// 连续心跳超时
|
||||
/// </summary>
|
||||
private int _heatBeatTimeoutNum = 0;
|
||||
|
||||
private int _ping = -1;
|
||||
|
||||
private float NowTime => GameTime.unscaledTime;
|
||||
|
||||
private string _lastHost = null;
|
||||
private int _lastPort = 0;
|
||||
|
||||
public GameClient()
|
||||
{
|
||||
_connectWatcher = new ClientConnectWatcher(this);
|
||||
_dispatcher = new MsgDispatcher();
|
||||
_dispatcher.SetTimeout(5f);
|
||||
GameEvent.AddEventListener<INetworkChannel,object>(NetworkEvent.NetworkConnectedEvent,OnNetworkConnected);
|
||||
GameEvent.AddEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent,OnNetworkClosed);
|
||||
GameEvent.AddEventListener<INetworkChannel,NetworkErrorCode,SocketError,string>(NetworkEvent.NetworkErrorEvent,OnNetworkError);
|
||||
GameEvent.AddEventListener<INetworkChannel,object>(NetworkEvent.NetworkCustomErrorEvent,OnNetworkCustomError);
|
||||
_channel = Network.Instance.CreateNetworkChannel("GameClient", ServiceType.Tcp, new NetworkChannelHelper());
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(INetworkChannel channel, object userdata)
|
||||
{
|
||||
bool isReconnect = (_status == GameClientStatus.StatusReconnect);
|
||||
//准备登录
|
||||
Status = GameClientStatus.StatusLogin;
|
||||
|
||||
OnServerConnected(isReconnect);
|
||||
}
|
||||
|
||||
private void OnNetworkClosed(INetworkChannel channel)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError, string errorMessage)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnNetworkCustomError(INetworkChannel channel, object userData)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Connect(string host, int port, bool reconnect = false)
|
||||
{
|
||||
ResetParam();
|
||||
if (!reconnect)
|
||||
{
|
||||
SetWatchReconnect(false);
|
||||
}
|
||||
|
||||
if (reconnect)
|
||||
{
|
||||
// GameEvent.Get<ICommUI>().ShowWaitUITip(WaitUISeq.LOGINWORLD_SEQID, G.R(TextDefine.ID_TIPS_RECONNECTING));
|
||||
}
|
||||
else
|
||||
{
|
||||
// GameEvent.Get<ICommUI>().ShowWaitUI(WaitUISeq.LOGINWORLD_SEQID);
|
||||
}
|
||||
|
||||
_lastHost = host;
|
||||
_lastPort = port;
|
||||
|
||||
Status = reconnect ? GameClientStatus.StatusReconnect : GameClientStatus.StatusInit;
|
||||
|
||||
_channel.Connect(host, port);
|
||||
}
|
||||
|
||||
public void Reconnect()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_lastHost) || _lastPort <= 0)
|
||||
{
|
||||
// GameModule.UI.ShowTipMsg("Invalid reconnect param");
|
||||
return;
|
||||
}
|
||||
|
||||
_connectWatcher.OnReConnect();
|
||||
Connect(_lastHost, _lastPort, true);
|
||||
}
|
||||
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_channel.Close();
|
||||
_status = GameClientStatus.StatusInit;
|
||||
}
|
||||
|
||||
public void OnServerConnected(bool isReconnect)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool SendCsMsg(CSPkg reqPkg)
|
||||
{
|
||||
if (!IsStatusCanSendMsg(reqPkg.Head.MsgId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoSendData(reqPkg);
|
||||
}
|
||||
|
||||
public bool IsStatusCanSendMsg(uint msgId)
|
||||
{
|
||||
bool canSend = false;
|
||||
if (_status == GameClientStatus.StatusLogin)
|
||||
{
|
||||
canSend = (msgId == (uint)CSMsgID.CsCmdActLoginReq);
|
||||
}
|
||||
|
||||
if (_status == GameClientStatus.StatusEnter)
|
||||
{
|
||||
canSend = true;
|
||||
}
|
||||
|
||||
if (!canSend)
|
||||
{
|
||||
float nowTime = NowTime;
|
||||
if (_lastLogDisconnectErrTime + 5 < nowTime)
|
||||
{
|
||||
Log.Error("GameClient not connected, send msg failed, msgId[{0}]", msgId);
|
||||
_lastLogDisconnectErrTime = nowTime;
|
||||
}
|
||||
|
||||
//UISys.Mgr.ShowTipMsg(TextDefine.ID_ERR_NETWORKD_DISCONNECT);
|
||||
}
|
||||
|
||||
return canSend;
|
||||
}
|
||||
|
||||
public bool SendCsMsg(CSPkg reqPkg, uint resCmd, CsMsgDelegate resHandler = null, bool needShowWaitUI = true)
|
||||
{
|
||||
if (!IsStatusCanSendMsg(reqPkg.Head.MsgId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var ret = DoSendData(reqPkg);
|
||||
if (!ret)
|
||||
{
|
||||
if (resHandler != null)
|
||||
{
|
||||
resHandler(CsMsgResult.InternalError, null);
|
||||
}
|
||||
|
||||
_dispatcher.NotifyCmdError(resCmd, CsMsgResult.InternalError);
|
||||
}
|
||||
else
|
||||
{
|
||||
//注册消息
|
||||
if (resHandler != null)
|
||||
{
|
||||
_dispatcher.RegSeqHandle(reqPkg.Head.Echo, resCmd, resHandler);
|
||||
if (reqPkg.Head.Echo > 0 && IsWaitingCmd(resCmd) && needShowWaitUI)
|
||||
{
|
||||
// TODO
|
||||
// GameEvent.Get<ICommUI>().ShowWaitUI(reqPkg.Head.Echo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool DoSendData(CSPkg reqPkg)
|
||||
{
|
||||
if (!IsIgnoreLog(reqPkg.Head.MsgId))
|
||||
{
|
||||
Log.Debug("[c-s] CmdId[{0}]\n{1}", reqPkg.Head.MsgId, reqPkg.Body.ToString());
|
||||
}
|
||||
var sendRet = _channel.Send(reqPkg);
|
||||
return sendRet;
|
||||
}
|
||||
|
||||
private bool IsIgnoreLog(uint msgId)
|
||||
{
|
||||
bool ignoreLog = false;
|
||||
switch (msgId)
|
||||
{
|
||||
case (uint)CSMsgID.CsCmdHeatbeatReq:
|
||||
case (uint)CSMsgID.CsCmdHeatbeatRes:
|
||||
ignoreLog = true;
|
||||
break;
|
||||
}
|
||||
return ignoreLog;
|
||||
}
|
||||
|
||||
public static bool IsWaitingCmd(uint msgId)
|
||||
{
|
||||
//心跳包不需要读条等待
|
||||
if (msgId == (uint)CSMsgID.CsCmdHeatbeatRes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ResetParam()
|
||||
{
|
||||
_lastLogDisconnectErrTime = 0f;
|
||||
_heatBeatTimeoutNum = 0;
|
||||
_lastHbTime = 0f;
|
||||
_ping = -1;
|
||||
_lastNetErrCode = 0;
|
||||
}
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
_dispatcher.Update();
|
||||
TickHeartBeat();
|
||||
CheckHeatBeatTimeout();
|
||||
_connectWatcher.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册静态消息
|
||||
/// </summary>
|
||||
/// <param name="iCmdID"></param>
|
||||
/// <param name="msgDelegate"></param>
|
||||
public void RegCmdHandle(int iCmdID, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
_dispatcher.RegCmdHandle((uint)iCmdID, msgDelegate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除消息处理函数
|
||||
/// </summary>
|
||||
/// <param name="cmdId"></param>
|
||||
/// <param name="msgDelegate"></param>
|
||||
public void RmvCmdHandle(int cmdId, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
_dispatcher.RmvCmdHandle((uint)cmdId, msgDelegate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置加密密钥
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public void SetEncryptKey(string key)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置是否需要监控网络重连。
|
||||
/// 登录成功后,开启监控,可以自动重连或者提示玩家重连。
|
||||
/// </summary>
|
||||
/// <param name="needWatch"></param>
|
||||
public void SetWatchReconnect(bool needWatch)
|
||||
{
|
||||
_connectWatcher.Enable = needWatch;
|
||||
}
|
||||
|
||||
public bool IsNetworkOkAndLogin()
|
||||
{
|
||||
return _status == GameClientStatus.StatusEnter;
|
||||
}
|
||||
|
||||
#region 心跳处理
|
||||
|
||||
/// <summary>
|
||||
/// 最近一次心跳的时间
|
||||
/// </summary>
|
||||
private float _lastHbTime = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// 心跳间隔
|
||||
/// </summary>
|
||||
private readonly float _heartBeatDurTime = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 心跳超时的最大次数
|
||||
/// </summary>
|
||||
private const int HeatBeatTimeoutMaxCount = 2;
|
||||
|
||||
private bool CheckHeatBeatTimeout()
|
||||
{
|
||||
if (_heatBeatTimeoutNum >= HeatBeatTimeoutMaxCount)
|
||||
{
|
||||
//断开连接
|
||||
Shutdown();
|
||||
|
||||
//准备重连
|
||||
_heatBeatTimeoutNum = 0;
|
||||
Status = GameClientStatus.StatusClose;
|
||||
Log.Error("heat beat detect timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TickHeartBeat()
|
||||
{
|
||||
if (Status != GameClientStatus.StatusEnter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var nowTime = NowTime;
|
||||
if (_lastHbTime + _heartBeatDurTime < nowTime)
|
||||
{
|
||||
_lastHbTime = nowTime;
|
||||
|
||||
CSPkg heatPkg = ProtobufUtility.BuildCsMsg((int)CSMsgID.CsCmdHeatbeatReq);
|
||||
heatPkg.Body.HeatBeatReq = new CSHeatBeatReq { HeatEchoTime = _lastHbTime };
|
||||
SendCsMsg(heatPkg, (int)CSMsgID.CsCmdHeatbeatRes, HandleHeatBeatRes);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleHeatBeatRes(CsMsgResult result, CSPkg msg)
|
||||
{
|
||||
if (result != CsMsgResult.NoError)
|
||||
{
|
||||
//如果是超时了,则标记最近收到包的次数
|
||||
if (result == CsMsgResult.MsgTimeOut)
|
||||
{
|
||||
_heatBeatTimeoutNum++;
|
||||
Log.Warning("heat beat timeout: {0}", _heatBeatTimeoutNum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var resBody = msg.Body.HeatBeatRes;
|
||||
float diffTime = NowTime - resBody.HeatEchoTime;
|
||||
_ping = (int)(diffTime * 1000);
|
||||
_heatBeatTimeoutNum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ping值
|
||||
|
||||
/// <summary>
|
||||
/// ping值
|
||||
/// </summary>
|
||||
public int Ping
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsPingValid())
|
||||
{
|
||||
return _ping / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPingValid()
|
||||
{
|
||||
if (IsNetworkOkAndLogin())
|
||||
{
|
||||
return _ping >= 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c5441725c9f4d98a7790dc76a6a0c48
|
||||
timeCreated: 1684331687
|
@@ -1,265 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameProto;
|
||||
using TEngine;
|
||||
using CSPkg = GameProto.CSPkg;
|
||||
|
||||
namespace GameLogic
|
||||
{
|
||||
internal class MsgHandleDataToRmv
|
||||
{
|
||||
public uint MsgId;
|
||||
public CsMsgDelegate Handle;
|
||||
};
|
||||
|
||||
class MsgDispatcher
|
||||
{
|
||||
const int CheckTimeoutPerframe = 10;
|
||||
const int MaxMsgHandle = 256;
|
||||
|
||||
private readonly CsMsgDelegate[] _aMsgHandles = new CsMsgDelegate[MaxMsgHandle];
|
||||
private readonly float[] _fMsgRegTime = new float[MaxMsgHandle];
|
||||
private readonly UInt32[] _adwMsgRegSeq = new UInt32[MaxMsgHandle]; //因为_aiMsgRegResCmdID存储的是hash,不能保证一定seqid一样,所以这儿存储下,用来校验
|
||||
private readonly uint[] _aiMsgRegResCmdID = new uint[MaxMsgHandle];
|
||||
|
||||
UInt32 _dwLastCheckIndex = 0;
|
||||
|
||||
private readonly Dictionary<uint, List<CsMsgDelegate>> _mapCmdHandle = new Dictionary<uint, List<CsMsgDelegate>>();
|
||||
private readonly List<CsMsgStatDelegate> _listStatHandle = new List<CsMsgStatDelegate>();
|
||||
|
||||
//防止在处理消息的时候又删除了消息映射,所以这儿加了个队列来做个保护
|
||||
private readonly List<MsgHandleDataToRmv> _rmvList = new List<MsgHandleDataToRmv>();
|
||||
private bool _isInHandleLoop = false;
|
||||
private float _timeout = 5;
|
||||
|
||||
// 清理所有的网络消息
|
||||
public void CleanAllNetMsg()
|
||||
{
|
||||
_mapCmdHandle.Clear();
|
||||
}
|
||||
|
||||
public void SetTimeout(float timeout)
|
||||
{
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
public void RegSeqHandle(UInt32 dwMsgSeqID, uint iResCmdID, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
UInt32 hashIndex = dwMsgSeqID % MaxMsgHandle;
|
||||
if (_aMsgHandles[hashIndex] != null)
|
||||
{
|
||||
OnCallSeqHandle(_adwMsgRegSeq[hashIndex], _aiMsgRegResCmdID[hashIndex]);
|
||||
NotifyTimeout(_aMsgHandles[hashIndex]);
|
||||
RmvReg((int)hashIndex);
|
||||
}
|
||||
|
||||
_aMsgHandles[hashIndex] = msgDelegate;
|
||||
_fMsgRegTime[hashIndex] = NowTime;
|
||||
_adwMsgRegSeq[hashIndex] = dwMsgSeqID;
|
||||
_aiMsgRegResCmdID[hashIndex] = iResCmdID;
|
||||
}
|
||||
|
||||
public void RegCmdHandle(uint iCmdID, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
if (!_mapCmdHandle.TryGetValue(iCmdID, out var listHandle))
|
||||
{
|
||||
listHandle = new List<CsMsgDelegate>();
|
||||
_mapCmdHandle[iCmdID] = listHandle;
|
||||
}
|
||||
|
||||
if (listHandle != null)
|
||||
{
|
||||
if (listHandle.Contains(msgDelegate))
|
||||
{
|
||||
Log.Error("-------------repeat RegCmdHandle:{0}-----------", iCmdID);
|
||||
}
|
||||
|
||||
listHandle.Add(msgDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册统计处理接口
|
||||
/// </summary>
|
||||
/// <param name="handler"></param>
|
||||
public void RegCmdStatHandle(CsMsgStatDelegate handler)
|
||||
{
|
||||
_listStatHandle.Add(handler);
|
||||
}
|
||||
|
||||
public void DispatchCmdStat(int cmdID, int pkgSize)
|
||||
{
|
||||
foreach (CsMsgStatDelegate handle in _listStatHandle)
|
||||
{
|
||||
handle(cmdID, pkgSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void RmvCmdHandle(uint iCmdID, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
if (_isInHandleLoop)
|
||||
{
|
||||
MsgHandleDataToRmv toRmvData = new MsgHandleDataToRmv();
|
||||
toRmvData.MsgId = iCmdID;
|
||||
toRmvData.Handle = msgDelegate;
|
||||
|
||||
_rmvList.Add(toRmvData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_mapCmdHandle.TryGetValue(iCmdID, out var listHandle))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (listHandle != null)
|
||||
{
|
||||
listHandle.Remove(msgDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyCmdError(uint iCmdID, CsMsgResult result)
|
||||
{
|
||||
NotifyCmdHandle(iCmdID, result, default(CSPkg));
|
||||
}
|
||||
|
||||
protected bool NotifyCmdHandle(uint cmdID, CsMsgResult result, CSPkg pkg)
|
||||
{
|
||||
bool ret = false;
|
||||
if (_mapCmdHandle.TryGetValue(cmdID, out var listHandle))
|
||||
{
|
||||
_isInHandleLoop = true;
|
||||
|
||||
var rmvList = _rmvList;
|
||||
rmvList.Clear();
|
||||
foreach (CsMsgDelegate handle in listHandle)
|
||||
{
|
||||
ret = true;
|
||||
|
||||
TProfiler.BeginSample("handle");
|
||||
handle(result, pkg);
|
||||
TProfiler.EndSample();
|
||||
}
|
||||
|
||||
_isInHandleLoop = false;
|
||||
|
||||
//再统一删除掉
|
||||
int rmvCnt = rmvList.Count;
|
||||
for (int i = 0; i < rmvCnt; i++)
|
||||
{
|
||||
var rmvItem = rmvList[i];
|
||||
Log.Error("-------------remove cmd handle on loop:{0}-----------", rmvItem.MsgId);
|
||||
RmvCmdHandle(rmvItem.MsgId, rmvItem.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected void OnCallSeqHandle(UInt32 echoSeq, uint resCmdID)
|
||||
{
|
||||
if (echoSeq > 0)
|
||||
{
|
||||
// TODO
|
||||
// GameEvent.Get<ICommUI>().FinWaitUI(echoSeq);
|
||||
}
|
||||
}
|
||||
|
||||
protected void NotifyTimeout(CsMsgDelegate msgHandler)
|
||||
{
|
||||
msgHandler(CsMsgResult.MsgTimeOut, default(CSPkg));
|
||||
}
|
||||
|
||||
public void NotifySeqError(UInt32 dwSeqID, CsMsgResult result)
|
||||
{
|
||||
UInt32 hashIndex = dwSeqID % MaxMsgHandle;
|
||||
|
||||
//先判断是否有注册的指定消息
|
||||
if (_aMsgHandles[hashIndex] != null &&
|
||||
_adwMsgRegSeq[hashIndex] == dwSeqID)
|
||||
{
|
||||
OnCallSeqHandle(dwSeqID, _aiMsgRegResCmdID[hashIndex]);
|
||||
_aMsgHandles[hashIndex](result, null);
|
||||
|
||||
RmvReg((int)hashIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCmdFilterNoLog(int cmdID)
|
||||
{
|
||||
switch (cmdID)
|
||||
{
|
||||
case (int)CSMsgID.CsCmdHeatbeatRes:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void NotifyMsg(CSPkg msg)
|
||||
{
|
||||
UInt32 dwSeq = msg.Head.Echo;
|
||||
UInt32 hashIndex = dwSeq % MaxMsgHandle;
|
||||
//判断是否有固定的消息处理流程
|
||||
bool bHaveHandle = NotifyCmdHandle(msg.Head.MsgId, CsMsgResult.NoError, msg);
|
||||
|
||||
//再判断是否有注册的指定消息
|
||||
if (_aMsgHandles[hashIndex] != null &&
|
||||
_adwMsgRegSeq[hashIndex] == dwSeq &&
|
||||
_aiMsgRegResCmdID[hashIndex] == (int)msg.Head.MsgId)
|
||||
{
|
||||
OnCallSeqHandle(_adwMsgRegSeq[hashIndex], _aiMsgRegResCmdID[hashIndex]);
|
||||
_aMsgHandles[hashIndex](CsMsgResult.NoError, msg);
|
||||
RmvReg((int)hashIndex);
|
||||
bHaveHandle = true;
|
||||
}
|
||||
|
||||
if (!bHaveHandle)
|
||||
{
|
||||
Log.Debug("there is no handle for Msg[{0}]", msg.Head.MsgId);
|
||||
}
|
||||
}
|
||||
|
||||
private float NowTime => GameTime.unscaledTime;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
CheckTimeOut();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定时检查是否请求超时。
|
||||
/// </summary>
|
||||
private void CheckTimeOut()
|
||||
{
|
||||
float timeout = _timeout;
|
||||
float nowTime = NowTime;
|
||||
for (int i = 0; i < CheckTimeoutPerframe; i++)
|
||||
{
|
||||
_dwLastCheckIndex = (_dwLastCheckIndex + 1) % MaxMsgHandle;
|
||||
if (_aMsgHandles[_dwLastCheckIndex] != null)
|
||||
{
|
||||
if (_fMsgRegTime[_dwLastCheckIndex] + timeout < nowTime)
|
||||
{
|
||||
Log.Error("msg timeout, resCmdID[{0}], reqSeq[{1}]", _aiMsgRegResCmdID[_dwLastCheckIndex],
|
||||
_adwMsgRegSeq[_dwLastCheckIndex]);
|
||||
|
||||
OnCallSeqHandle(_adwMsgRegSeq[_dwLastCheckIndex], _aiMsgRegResCmdID[_dwLastCheckIndex]);
|
||||
NotifyTimeout(_aMsgHandles[_dwLastCheckIndex]);
|
||||
|
||||
RmvReg((int)_dwLastCheckIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RmvReg(int index)
|
||||
{
|
||||
_aMsgHandles[index] = null;
|
||||
_adwMsgRegSeq[index] = 0;
|
||||
_aiMsgRegResCmdID[index] = 0;
|
||||
_fMsgRegTime[index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d667fb84fed4c5f93a06c464585512f
|
||||
timeCreated: 1684333223
|
@@ -1,226 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using GameProto;
|
||||
using Google.Protobuf;
|
||||
using TEngine;
|
||||
|
||||
namespace GameLogic
|
||||
{
|
||||
public class NetworkChannelHelper : INetworkChannelHelper, IMemory
|
||||
{
|
||||
private readonly Dictionary<int, Type> _serverToClientPacketTypes = new Dictionary<int, Type>();
|
||||
private readonly MemoryStream _cachedStream = new MemoryStream(1024 * 8);
|
||||
private INetworkChannel _networkChannel = null;
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息包头长度。
|
||||
/// <remarks>4。</remarks>
|
||||
/// </summary>
|
||||
public int PacketHeaderLength => sizeof(int);
|
||||
|
||||
/// <summary>
|
||||
/// 初始化网络频道辅助器。
|
||||
/// </summary>
|
||||
/// <param name="networkChannel">网络频道。</param>
|
||||
public void Initialize(INetworkChannel networkChannel)
|
||||
{
|
||||
_networkChannel = networkChannel;
|
||||
|
||||
GameEvent.AddEventListener<INetworkChannel, object>(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected);
|
||||
GameEvent.AddEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent, OnNetworkClosed);
|
||||
GameEvent.AddEventListener<INetworkChannel, int>(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat);
|
||||
GameEvent.AddEventListener<INetworkChannel, NetworkErrorCode, SocketError, string>(NetworkEvent.NetworkErrorEvent, OnNetworkError);
|
||||
GameEvent.AddEventListener<INetworkChannel, object>(NetworkEvent.NetworkCustomErrorEvent, OnNetworkCustomError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭并清理网络频道辅助器。
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
{
|
||||
GameEvent.RemoveEventListener<INetworkChannel, object>(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected);
|
||||
GameEvent.RemoveEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent, OnNetworkClosed);
|
||||
GameEvent.RemoveEventListener<INetworkChannel, int>(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat);
|
||||
GameEvent.RemoveEventListener<INetworkChannel, NetworkErrorCode, SocketError, string>(NetworkEvent.NetworkErrorEvent, OnNetworkError);
|
||||
GameEvent.RemoveEventListener<INetworkChannel, object>(NetworkEvent.NetworkCustomErrorEvent, OnNetworkCustomError);
|
||||
|
||||
_networkChannel = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备进行连接。
|
||||
/// </summary>
|
||||
public void PrepareForConnecting()
|
||||
{
|
||||
_networkChannel.Socket.ReceiveBufferSize = 1024 * 64;
|
||||
_networkChannel.Socket.SendBufferSize = 1024 * 64;
|
||||
}
|
||||
|
||||
public CSPkg HeartBeatPack = new CSPkg { Head = new CSPkgHead(), Body = new CSPkgBody() };
|
||||
|
||||
/// <summary>
|
||||
/// 发送心跳消息包。
|
||||
/// </summary>
|
||||
/// <returns>是否发送心跳消息包成功。</returns>
|
||||
public bool SendHeartBeat()
|
||||
{
|
||||
HeartBeatPack.Head.MsgId = (uint)CSMsgID.CsCmdHeatbeatReq;
|
||||
_networkChannel.Send(HeartBeatPack);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化消息包。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息包类型。</typeparam>
|
||||
/// <param name="packet">要序列化的消息包。</param>
|
||||
/// <param name="destination">要序列化的目标流。</param>
|
||||
/// <returns>是否序列化成功。</returns>
|
||||
public bool Serialize(CSPkg packet, Stream destination)
|
||||
{
|
||||
if (packet == null)
|
||||
{
|
||||
Log.Warning("Packet is invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_cachedStream.SetLength(_cachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入
|
||||
_cachedStream.Position = 0L;
|
||||
global::ProtobufUtility.ToStreamWithHead(packet,_cachedStream);
|
||||
_cachedStream.WriteTo(destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包头。
|
||||
/// </summary>
|
||||
/// <param name="source">要反序列化的来源流。</param>
|
||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||
/// <returns>反序列化后的消息包头。</returns>
|
||||
public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
|
||||
{
|
||||
// 注意:此函数并不在主线程调用!
|
||||
customErrorData = null;
|
||||
PacketHeader packetHeader = MemoryPool.Acquire<PacketHeader>();
|
||||
packetHeader.PacketLength = ((MemoryStream)source).GetBuffer()[0];
|
||||
return packetHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包。
|
||||
/// </summary>
|
||||
/// <param name="packetHeader">消息包头。</param>
|
||||
/// <param name="source">要反序列化的来源流。</param>
|
||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||
/// <returns>反序列化后的消息包。</returns>
|
||||
public CSPkg DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
|
||||
{
|
||||
// 注意:此函数并不在主线程调用!
|
||||
customErrorData = null;
|
||||
|
||||
PacketHeader scPacketHeader = packetHeader as PacketHeader;
|
||||
if (scPacketHeader == null)
|
||||
{
|
||||
Log.Warning("Packet header is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
CSPkg csPkg = null;
|
||||
if (scPacketHeader.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
csPkg = global::ProtobufUtility.Deserialize(((MemoryStream)source).GetBuffer(),0,scPacketHeader.PacketLength);
|
||||
Log.Debug("[s-c] CmdId[{0}]\n{1}", csPkg.Head.MsgId, csPkg.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Packet header is invalid.");
|
||||
}
|
||||
|
||||
MemoryPool.Release(scPacketHeader);
|
||||
return csPkg;
|
||||
}
|
||||
|
||||
private Type GetServerToClientPacketType(int id)
|
||||
{
|
||||
if (_serverToClientPacketTypes.TryGetValue(id, out var type))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(INetworkChannel channel, object userdata)
|
||||
{
|
||||
if (channel != _networkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Network channel '{0}' connected, local address '{1}', remote address '{2}'.",
|
||||
channel.Name, channel.Socket.LocalEndPoint.ToString(),
|
||||
channel.Socket.RemoteEndPoint.ToString());
|
||||
}
|
||||
|
||||
private void OnNetworkClosed(INetworkChannel channel)
|
||||
{
|
||||
if (channel != _networkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Network channel '{0}' closed.", channel.Name);
|
||||
}
|
||||
|
||||
private void OnNetworkMissHeartBeat(INetworkChannel channel, int missCount)
|
||||
{
|
||||
if (channel != _networkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Fatal("Network channel '{0}' miss heart beat '{1}' times.", channel.Name, missCount.ToString());
|
||||
|
||||
if (missCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
channel.Close();
|
||||
}
|
||||
|
||||
private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError, string errorMessage)
|
||||
{
|
||||
if (channel != _networkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Fatal("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", channel.Name, networkErrorCode.ToString(), errorMessage);
|
||||
|
||||
channel.Close();
|
||||
}
|
||||
|
||||
private void OnNetworkCustomError(INetworkChannel channel, object userData)
|
||||
{
|
||||
if (channel != _networkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf86ce2ddfb5429abecfb06257c86acd
|
||||
timeCreated: 1682045961
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e77590d750cf480baae2468038654bc0
|
||||
timeCreated: 1681991042
|
@@ -1,23 +0,0 @@
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络地址类型。
|
||||
/// </summary>
|
||||
public enum AddressFamily : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 未知。
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// IP 版本 4。
|
||||
/// </summary>
|
||||
IPv4,
|
||||
|
||||
/// <summary>
|
||||
/// IP 版本 6。
|
||||
/// </summary>
|
||||
IPv6
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd8e805430d24bdb8c2679b59ba7c2d6
|
||||
timeCreated: 1681993653
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9aefe7f3e0fd485091a20579b29872a7
|
||||
timeCreated: 1681994393
|
@@ -1,180 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using GameProto;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络频道接口。
|
||||
/// </summary>
|
||||
public interface INetworkChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取网络频道名称。
|
||||
/// </summary>
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络频道所使用的 Socket。
|
||||
/// </summary>
|
||||
Socket Socket
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否已连接。
|
||||
/// </summary>
|
||||
bool Connected
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络服务类型。
|
||||
/// </summary>
|
||||
ServiceType ServiceType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络地址类型。
|
||||
/// </summary>
|
||||
AddressFamily AddressFamily
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取要发送的消息包数量。
|
||||
/// </summary>
|
||||
int SendPacketCount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取累计发送的消息包数量。
|
||||
/// </summary>
|
||||
int SentPacketCount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取已接收未处理的消息包数量。
|
||||
/// </summary>
|
||||
int ReceivePacketCount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取累计已接收的消息包数量。
|
||||
/// </summary>
|
||||
int ReceivedPacketCount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置当收到消息包时是否重置心跳流逝时间。
|
||||
/// </summary>
|
||||
bool ResetHeartBeatElapseSecondsWhenReceivePacket
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取丢失心跳的次数。
|
||||
/// </summary>
|
||||
int MissHeartBeatCount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置心跳间隔时长,以秒为单位。
|
||||
/// </summary>
|
||||
float HeartBeatInterval
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取心跳等待时长,以秒为单位。
|
||||
/// </summary>
|
||||
float HeartBeatElapseSeconds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册网络消息包处理函数。
|
||||
/// </summary>
|
||||
/// <param name="msgId">网络消息包id。</param>
|
||||
/// <param name="msgDelegate">要注册的网络消息包处理函数。</param>
|
||||
/// <param name="checkRepeat">是否检测重复。</param>
|
||||
void RegisterMsgHandler(int msgId, CsMsgDelegate msgDelegate, bool checkRepeat = false);
|
||||
|
||||
/// <summary>
|
||||
/// 移除网络消息包处理函数。
|
||||
/// </summary>
|
||||
/// <param name="msgId">网络消息包id。</param>
|
||||
/// <param name="msgDelegate">要注册的网络消息包处理函数。</param>
|
||||
void RemoveMsgHandler(int msgId, CsMsgDelegate msgDelegate);
|
||||
|
||||
/// <summary>
|
||||
/// 连接到远程主机。
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
||||
/// <param name="port">远程主机的端口号。</param>
|
||||
void Connect(string ipAddress, int port);
|
||||
|
||||
/// <summary>
|
||||
/// 连接到远程主机。
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
||||
/// <param name="port">远程主机的端口号。</param>
|
||||
void Connect(IPAddress ipAddress, int port);
|
||||
|
||||
/// <summary>
|
||||
/// 连接到远程主机。
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
||||
/// <param name="port">远程主机的端口号。</param>
|
||||
/// <param name="userData">用户自定义数据。</param>
|
||||
void Connect(IPAddress ipAddress, int port, object userData);
|
||||
|
||||
/// <summary>
|
||||
/// 关闭网络频道。
|
||||
/// </summary>
|
||||
void Close();
|
||||
|
||||
/// <summary>
|
||||
/// 向远程主机发送消息包。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息包类型。</typeparam>
|
||||
/// <param name="packet">要发送的消息包。</param>
|
||||
/// <returns>消息包是否发送成功。</returns>
|
||||
bool Send(CSPkg packet);
|
||||
|
||||
/// <summary>
|
||||
/// 向远程主机发送消息包并注册消息回调。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息包类型。</typeparam>
|
||||
/// <param name="packet">要发送的消息包。</param>
|
||||
/// <param name="resHandler">要注册的回调。</param>
|
||||
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
||||
/// <returns>消息包是否发送成功。</returns>
|
||||
bool Send(CSPkg packet, CsMsgDelegate resHandler, bool needShowWaitUI = false);
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09b554ed41c546a1bc40e5be392f836a
|
||||
timeCreated: 1681993830
|
@@ -1,67 +0,0 @@
|
||||
using System.IO;
|
||||
using GameProto;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络频道辅助器接口。
|
||||
/// </summary>
|
||||
public interface INetworkChannelHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取消息包头长度。
|
||||
/// </summary>
|
||||
int PacketHeaderLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化网络频道辅助器。
|
||||
/// </summary>
|
||||
/// <param name="networkChannel">网络频道。</param>
|
||||
void Initialize(INetworkChannel networkChannel);
|
||||
|
||||
/// <summary>
|
||||
/// 关闭并清理网络频道辅助器。
|
||||
/// </summary>
|
||||
void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// 准备进行连接。
|
||||
/// </summary>
|
||||
void PrepareForConnecting();
|
||||
|
||||
/// <summary>
|
||||
/// 发送心跳消息包。
|
||||
/// </summary>
|
||||
/// <returns>是否发送心跳消息包成功。</returns>
|
||||
bool SendHeartBeat();
|
||||
|
||||
/// <summary>
|
||||
/// 序列化消息包。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息包类型。</typeparam>
|
||||
/// <param name="packet">要序列化的消息包。</param>
|
||||
/// <param name="destination">要序列化的目标流。</param>
|
||||
/// <returns>是否序列化成功。</returns>
|
||||
bool Serialize(CSPkg packet, Stream destination);
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包头。
|
||||
/// </summary>
|
||||
/// <param name="source">要反序列化的来源流。</param>
|
||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||
/// <returns>反序列化后的消息包头。</returns>
|
||||
IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData);
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包。
|
||||
/// </summary>
|
||||
/// <param name="packetHeader">消息包头。</param>
|
||||
/// <param name="source">要反序列化的来源流。</param>
|
||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||
/// <returns>反序列化后的消息包。</returns>
|
||||
CSPkg DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData);
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f847791db5a4e6cbbc39dd56a6888d9
|
||||
timeCreated: 1681993713
|
@@ -1,84 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络管理器接口。
|
||||
/// </summary>
|
||||
public interface INetworkManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取网络频道数量。
|
||||
/// </summary>
|
||||
int NetworkChannelCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 网络连接成功事件。
|
||||
/// </summary>
|
||||
event Action<INetworkChannel, object> NetworkConnected;
|
||||
|
||||
/// <summary>
|
||||
/// 网络连接关闭事件。
|
||||
/// </summary>
|
||||
event Action<INetworkChannel> NetworkClosed;
|
||||
|
||||
/// <summary>
|
||||
/// 网络心跳包丢失事件。
|
||||
/// </summary>
|
||||
event Action<INetworkChannel, int> NetworkMissHeartBeat;
|
||||
|
||||
/// <summary>
|
||||
/// 网络错误事件。
|
||||
/// </summary>
|
||||
event Action<INetworkChannel, NetworkErrorCode, SocketError, string> NetworkError;
|
||||
|
||||
/// <summary>
|
||||
/// 用户自定义网络错误事件。
|
||||
/// </summary>
|
||||
event Action<INetworkChannel, object> NetworkCustomError;
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否存在网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <returns>是否存在网络频道。</returns>
|
||||
bool HasNetworkChannel(string name);
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <returns>要获取的网络频道。</returns>
|
||||
INetworkChannel GetNetworkChannel(string name);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络频道。
|
||||
/// </summary>
|
||||
/// <returns>所有网络频道。</returns>
|
||||
INetworkChannel[] GetAllNetworkChannels();
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络频道。
|
||||
/// </summary>
|
||||
/// <param name="results">所有网络频道。</param>
|
||||
void GetAllNetworkChannels(List<INetworkChannel> results);
|
||||
|
||||
/// <summary>
|
||||
/// 创建网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <param name="serviceType">网络服务类型。</param>
|
||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
||||
/// <returns>要创建的网络频道。</returns>
|
||||
INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper);
|
||||
|
||||
/// <summary>
|
||||
/// 销毁网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <returns>是否销毁网络频道成功。</returns>
|
||||
bool DestroyNetworkChannel(string name);
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7de8458fc50e4cd3b49ef73d35ad9763
|
||||
timeCreated: 1681993806
|
@@ -1,16 +0,0 @@
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络消息包头接口。
|
||||
/// </summary>
|
||||
public interface IPacketHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取网络消息包长度。
|
||||
/// </summary>
|
||||
int PacketLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7659e4a617d84126b0a548077fb4fdcc
|
||||
timeCreated: 1681994226
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d988d26345f402f9177488d8921184f
|
||||
timeCreated: 1682092063
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6dff8f4f5dd94df49bf6d616fb11cc9c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,71 +0,0 @@
|
||||
using System.Buffers;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于调试的KCP IO 类,没有Kcp功能
|
||||
/// </summary>
|
||||
public class FakeKcpIO : IKcpIO
|
||||
{
|
||||
QueuePipe<byte[]> recv = new QueuePipe<byte[]>();
|
||||
public int Input(ReadOnlySpan<byte> span)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
recv.Write(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Input(ReadOnlySequence<byte> span)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
return Input(buffer);
|
||||
}
|
||||
|
||||
public async UniTask RecvAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var buffer = await recv.ReadAsync().ConfigureAwait(false);
|
||||
var target = writer.GetMemory(buffer.Length);
|
||||
buffer.AsSpan().CopyTo(target.Span);
|
||||
writer.Advance(buffer.Length);
|
||||
}
|
||||
|
||||
public async UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null)
|
||||
{
|
||||
var temp = await recv.ReadAsync().ConfigureAwait(false);
|
||||
temp.AsSpan().CopyTo(buffer);
|
||||
return temp.Length;
|
||||
}
|
||||
|
||||
QueuePipe<byte[]> send = new QueuePipe<byte[]>();
|
||||
public int Send(ReadOnlySpan<byte> span, object options = null)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
send.Write(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Send(ReadOnlySequence<byte> span, object options = null)
|
||||
{
|
||||
byte[] buffer = new byte[span.Length];
|
||||
span.CopyTo(buffer);
|
||||
return Send(buffer);
|
||||
}
|
||||
|
||||
public async UniTask OutputAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var buffer = await send.ReadAsync().ConfigureAwait(false);
|
||||
Write(writer, buffer);
|
||||
}
|
||||
|
||||
private static void Write(IBufferWriter<byte> writer, byte[] buffer)
|
||||
{
|
||||
var span = writer.GetSpan(buffer.Length);
|
||||
buffer.AsSpan().CopyTo(span);
|
||||
writer.Advance(buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3c2f9de3c34ffc409549b86ac3fc530
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,147 +0,0 @@
|
||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
||||
using System.Buffers;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// Kcp回调
|
||||
/// </summary>
|
||||
public interface IKcpCallback
|
||||
{
|
||||
/// <summary>
|
||||
/// kcp 发送方向输出
|
||||
/// </summary>
|
||||
/// <param name="buffer">kcp 交出发送缓冲区控制权,缓冲区来自<see cref="RentBuffer(int)"/></param>
|
||||
/// <param name="avalidLength">数据的有效长度</param>
|
||||
/// <returns>不需要返回值</returns>
|
||||
/// <remarks>通过增加 avalidLength 能够在协议栈中有效的减少数据拷贝</remarks>
|
||||
void Output(BufferOwner buffer, int avalidLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kcp回调
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 失败设计,<see cref="KcpOutputWriter.Output(BufferOwner, int)"/>。IMemoryOwner是没有办法代替的。
|
||||
/// 这里只相当于把 IKcpCallback 和 IRentable 和并。
|
||||
/// </remarks>
|
||||
public interface IKcpOutputWriter : IBufferWriter<byte>
|
||||
{
|
||||
int UnflushedBytes { get; }
|
||||
void Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 外部提供缓冲区,可以在外部链接一个内存池
|
||||
/// </summary>
|
||||
public interface IRentable
|
||||
{
|
||||
/// <summary>
|
||||
/// 外部提供缓冲区,可以在外部链接一个内存池
|
||||
/// </summary>
|
||||
BufferOwner RentBuffer(int length);
|
||||
}
|
||||
|
||||
public interface IKcpSetting
|
||||
{
|
||||
int Interval(int interval);
|
||||
/// <summary>
|
||||
/// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
|
||||
/// </summary>
|
||||
/// <param name="nodelay">0:disable(default), 1:enable</param>
|
||||
/// <param name="interval">internal update timer interval in millisec, default is 100ms</param>
|
||||
/// <param name="resend">0:disable fast resend(default), 1:enable fast resend</param>
|
||||
/// <param name="nc">0:normal congestion control(default), 1:disable congestion control</param>
|
||||
/// <returns></returns>
|
||||
int NoDelay(int nodelay, int interval, int resend, int nc);
|
||||
/// <summary>
|
||||
/// change MTU size, default is 1400
|
||||
/// <para>** 这个方法不是线程安全的。请在没有发送和接收时调用 。</para>
|
||||
/// </summary>
|
||||
/// <param name="mtu"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 如果没有必要,不要修改Mtu。过小的Mtu会导致分片数大于接收窗口,造成kcp阻塞冻结。
|
||||
/// </remarks>
|
||||
int SetMtu(int mtu = 1400);
|
||||
/// <summary>
|
||||
/// set maximum window size: sndwnd=32, rcvwnd=128 by default
|
||||
/// </summary>
|
||||
/// <param name="sndwnd"></param>
|
||||
/// <param name="rcvwnd"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 如果没有必要请不要修改。注意确保接收窗口必须大于最大分片数。
|
||||
/// </remarks>
|
||||
int WndSize(int sndwnd = 32, int rcvwnd = 128);
|
||||
}
|
||||
|
||||
public interface IKcpUpdate
|
||||
{
|
||||
void Update(in DateTimeOffset time);
|
||||
}
|
||||
|
||||
public interface IKcpSendable
|
||||
{
|
||||
/// <summary>
|
||||
/// 将要发送到网络的数据Send到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="options"></param>
|
||||
int Send(ReadOnlySpan<byte> span, object options = null);
|
||||
/// <summary>
|
||||
/// 将要发送到网络的数据Send到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
/// <param name="options"></param>
|
||||
int Send(ReadOnlySequence<byte> span, object options = null);
|
||||
}
|
||||
|
||||
public interface IKcpInputable
|
||||
{
|
||||
/// <summary>
|
||||
/// 下层收到数据后添加到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
int Input(ReadOnlySpan<byte> span);
|
||||
/// <summary>
|
||||
/// 下层收到数据后添加到kcp协议中
|
||||
/// </summary>
|
||||
/// <param name="span"></param>
|
||||
int Input(ReadOnlySequence<byte> span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// kcp协议输入输出标准接口
|
||||
/// </summary>
|
||||
public interface IKcpIO : IKcpSendable, IKcpInputable
|
||||
{
|
||||
/// <summary>
|
||||
/// 从kcp中取出一个整合完毕的数据包
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
UniTask RecvAsync(IBufferWriter<byte> writer, object options = null);
|
||||
|
||||
/// <summary>
|
||||
/// 从kcp中取出一个整合完毕的数据包
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns>接收数据长度</returns>
|
||||
UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null);
|
||||
|
||||
/// <summary>
|
||||
/// 从kcp协议中取出需要发送到网络的数据。
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
UniTask OutputAsync(IBufferWriter<byte> writer, object options = null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0ca98e42147b3948bc8d30f24e9c366
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,88 +0,0 @@
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// Kcp报头
|
||||
/// https://zhuanlan.zhihu.com/p/559191428
|
||||
/// </summary>
|
||||
public interface IKcpHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话编号,两方一致才会通信
|
||||
/// </summary>
|
||||
uint conv { get; set; }
|
||||
/// <summary>
|
||||
/// 指令类型
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para/> IKCP_CMD_PUSH = 81 // cmd: push data 数据报文
|
||||
/// <para/> IKCP_CMD_ACK = 82 // cmd: ack 确认报文
|
||||
/// <para/> IKCP_CMD_WASK = 83 // cmd: window probe (ask) 窗口探测报文,询问对端剩余接收窗口的大小.
|
||||
/// <para/> IKCP_CMD_WINS = 84 // cmd: window size (tell) 窗口通知报文,通知对端剩余接收窗口的大小.
|
||||
/// </remarks>
|
||||
byte cmd { get; set; }
|
||||
/// <summary>
|
||||
/// 剩余分片数量,表示随后还有多少个报文属于同一个包。
|
||||
/// </summary>
|
||||
byte frg { get; set; }
|
||||
/// <summary>
|
||||
/// 自己可用窗口大小
|
||||
/// </summary>
|
||||
ushort wnd { get; set; }
|
||||
/// <summary>
|
||||
/// 发送时的时间戳 <seealso cref="DateTimeOffset.ToUnixTimeMilliseconds"/>
|
||||
/// </summary>
|
||||
uint ts { get; set; }
|
||||
/// <summary>
|
||||
/// 编号 确认编号或者报文编号
|
||||
/// </summary>
|
||||
uint sn { get; set; }
|
||||
/// <summary>
|
||||
/// 代表编号前面的所有报都收到了的标志
|
||||
/// </summary>
|
||||
uint una { get; set; }
|
||||
/// <summary>
|
||||
/// 数据内容长度
|
||||
/// </summary>
|
||||
uint len { get; }
|
||||
}
|
||||
public interface IKcpSegment : IKcpHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// 重传的时间戳。超过当前时间重发这个包
|
||||
/// </summary>
|
||||
uint resendts { get; set; }
|
||||
/// <summary>
|
||||
/// 超时重传时间,根据网络去定
|
||||
/// </summary>
|
||||
uint rto { get; set; }
|
||||
/// <summary>
|
||||
/// 快速重传机制,记录被跳过的次数,超过次数进行快速重传
|
||||
/// </summary>
|
||||
uint fastack { get; set; }
|
||||
/// <summary>
|
||||
/// 重传次数
|
||||
/// </summary>
|
||||
uint xmit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据内容
|
||||
/// </summary>
|
||||
Span<byte> data { get; }
|
||||
/// <summary>
|
||||
/// 将IKcpSegment编码成字节数组,并返回总长度(包括Kcp报头)
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
int Encode(Span<byte> buffer);
|
||||
}
|
||||
|
||||
public interface ISegmentManager<Segment> where Segment : IKcpSegment
|
||||
{
|
||||
Segment Alloc(int appendDateSize);
|
||||
void Free(Segment seg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9259bb6b548e46459cc766d8fe2faeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,387 +0,0 @@
|
||||
using System.Buffers;
|
||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public class Kcp<Segment> : KcpCore<Segment>
|
||||
where Segment : IKcpSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// create a new kcp control object, 'conv' must equal in two endpoint
|
||||
/// from the same connection.
|
||||
/// </summary>
|
||||
/// <param name="conv_"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="rentable">可租用内存的回调</param>
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_)
|
||||
{
|
||||
callbackHandle = callback;
|
||||
this.rentable = rentable;
|
||||
}
|
||||
|
||||
|
||||
//extension 重构和新增加的部分============================================
|
||||
|
||||
IRentable rentable;
|
||||
/// <summary>
|
||||
/// 如果外部能够提供缓冲区则使用外部缓冲区,否则new byte[]
|
||||
/// </summary>
|
||||
/// <param name="needSize"></param>
|
||||
/// <returns></returns>
|
||||
internal protected override BufferOwner CreateBuffer(int needSize)
|
||||
{
|
||||
var res = rentable?.RentBuffer(needSize);
|
||||
if (res == null)
|
||||
{
|
||||
return base.CreateBuffer(needSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Memory.Length < needSize)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(rentable.RentBuffer)} 指定的委托不符合标准,返回的" +
|
||||
$"BufferOwner.Memory.Length 小于 {nameof(needSize)}");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
||||
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
||||
/// <para/>所以只需要部分加锁即可。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public (BufferOwner buffer, int avalidLength) TryRecv()
|
||||
{
|
||||
var peekSize = -1;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return (null, -1);
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
peekSize = (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return (null, -1);
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
foreach (var item in rcv_queue)
|
||||
{
|
||||
length += item.len;
|
||||
if (item.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
peekSize = (int)length;
|
||||
|
||||
if (peekSize <= 0)
|
||||
{
|
||||
return (null, -2);
|
||||
}
|
||||
}
|
||||
|
||||
var buffer = CreateBuffer(peekSize);
|
||||
var recvlength = UncheckRecv(buffer.Memory.Span);
|
||||
return (buffer, recvlength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
||||
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
||||
/// <para/>所以只需要部分加锁即可。
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <returns></returns>
|
||||
public int TryRecv(IBufferWriter<byte> writer)
|
||||
{
|
||||
var peekSize = -1;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return -1;
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
peekSize = (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
foreach (var item in rcv_queue)
|
||||
{
|
||||
length += item.len;
|
||||
if (item.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
peekSize = (int)length;
|
||||
|
||||
if (peekSize <= 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return UncheckRecv(writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
public int Recv(Span<byte> buffer)
|
||||
{
|
||||
if (0 == rcv_queue.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var peekSize = PeekSize();
|
||||
if (peekSize < 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (peekSize > buffer.Length)
|
||||
{
|
||||
return -3;
|
||||
}
|
||||
|
||||
/// 拆分函数
|
||||
var recvLength = UncheckRecv(buffer);
|
||||
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <returns></returns>
|
||||
public int Recv(IBufferWriter<byte> writer)
|
||||
{
|
||||
if (0 == rcv_queue.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var peekSize = PeekSize();
|
||||
if (peekSize < 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
//if (peekSize > buffer.Length)
|
||||
//{
|
||||
// return -3;
|
||||
//}
|
||||
|
||||
/// 拆分函数
|
||||
var recvLength = UncheckRecv(writer);
|
||||
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这个函数不检查任何参数
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
int UncheckRecv(Span<byte> buffer)
|
||||
{
|
||||
var recover = false;
|
||||
if (rcv_queue.Count >= rcv_wnd)
|
||||
{
|
||||
recover = true;
|
||||
}
|
||||
|
||||
#region merge fragment.
|
||||
/// merge fragment.
|
||||
|
||||
var recvLength = 0;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var seg in rcv_queue)
|
||||
{
|
||||
seg.data.CopyTo(buffer.Slice(recvLength));
|
||||
recvLength += (int)seg.len;
|
||||
|
||||
count++;
|
||||
int frg = seg.frg;
|
||||
|
||||
SegmentManager.Free(seg);
|
||||
if (frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
rcv_queue.RemoveRange(0, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Move_Rcv_buf_2_Rcv_queue();
|
||||
|
||||
#region fast recover
|
||||
/// fast recover
|
||||
if (rcv_queue.Count < rcv_wnd && recover)
|
||||
{
|
||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||||
// tell remote my window size
|
||||
probe |= IKCP_ASK_TELL;
|
||||
}
|
||||
#endregion
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这个函数不检查任何参数
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <returns></returns>
|
||||
int UncheckRecv(IBufferWriter<byte> writer)
|
||||
{
|
||||
var recover = false;
|
||||
if (rcv_queue.Count >= rcv_wnd)
|
||||
{
|
||||
recover = true;
|
||||
}
|
||||
|
||||
#region merge fragment.
|
||||
/// merge fragment.
|
||||
|
||||
var recvLength = 0;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var seg in rcv_queue)
|
||||
{
|
||||
var len = (int)seg.len;
|
||||
var destination = writer.GetSpan(len);
|
||||
|
||||
seg.data.CopyTo(destination);
|
||||
writer.Advance(len);
|
||||
|
||||
recvLength += len;
|
||||
|
||||
count++;
|
||||
int frg = seg.frg;
|
||||
|
||||
SegmentManager.Free(seg);
|
||||
if (frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
rcv_queue.RemoveRange(0, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Move_Rcv_buf_2_Rcv_queue();
|
||||
|
||||
#region fast recover
|
||||
/// fast recover
|
||||
if (rcv_queue.Count < rcv_wnd && recover)
|
||||
{
|
||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||||
// tell remote my window size
|
||||
probe |= IKCP_ASK_TELL;
|
||||
}
|
||||
#endregion
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check the size of next message in the recv queue
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int PeekSize()
|
||||
{
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return -1;
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
return (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
foreach (var seg in rcv_queue)
|
||||
{
|
||||
length += seg.len;
|
||||
if (seg.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3840e90e37f0194bbfafb564ff80d45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcc0172c723e55a4389ad2f62b68a3fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,262 +0,0 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="IPipe{T}"/>
|
||||
/// <para></para>这是个简单的实现,更复杂使用微软官方实现<see cref="Channel.CreateBounded{T}(int)"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class QueuePipe<T> : Queue<T>
|
||||
{
|
||||
readonly object _innerLock = new object();
|
||||
private TaskCompletionSource<T> source;
|
||||
|
||||
//线程同步上下文由Task机制保证,无需额外处理
|
||||
//SynchronizationContext callbackContext;
|
||||
//public bool UseSynchronizationContext { get; set; } = true;
|
||||
|
||||
public virtual void Write(T item)
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
Enqueue(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
throw new Exception("内部顺序错误,不应该出现,请联系作者");
|
||||
}
|
||||
|
||||
var next = source;
|
||||
source = null;
|
||||
next.TrySetResult(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new void Enqueue(T item)
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
base.Enqueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
var res = Dequeue();
|
||||
var next = source;
|
||||
source = null;
|
||||
next?.TrySetResult(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Task<T> ReadAsync()
|
||||
{
|
||||
lock (_innerLock)
|
||||
{
|
||||
if (this.Count > 0)
|
||||
{
|
||||
var next = Dequeue();
|
||||
return Task.FromResult(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
source = new TaskCompletionSource<T>();
|
||||
return source.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTask<T> ReadValueTaskAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO<Segment> : KcpCore<Segment>, IKcpIO
|
||||
where Segment : IKcpSegment
|
||||
{
|
||||
OutputQ outq;
|
||||
|
||||
public KcpIO(uint conv_) : base(conv_)
|
||||
{
|
||||
outq = new OutputQ();
|
||||
callbackHandle = outq;
|
||||
}
|
||||
|
||||
internal override void Parse_data(Segment newseg)
|
||||
{
|
||||
base.Parse_data(newseg);
|
||||
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var recover = false;
|
||||
if (rcv_queue.Count >= rcv_wnd)
|
||||
{
|
||||
recover = true;
|
||||
}
|
||||
|
||||
while (TryRecv(out var arraySegment) > 0)
|
||||
{
|
||||
recvSignal.Enqueue(arraySegment);
|
||||
}
|
||||
|
||||
recvSignal.Flush();
|
||||
|
||||
#region fast recover
|
||||
|
||||
/// fast recover
|
||||
if (rcv_queue.Count < rcv_wnd && recover)
|
||||
{
|
||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||||
// tell remote my window size
|
||||
probe |= IKCP_ASK_TELL;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
QueuePipe<ArraySegment<Segment>> recvSignal = new QueuePipe<ArraySegment<Segment>>();
|
||||
|
||||
internal int TryRecv(out ArraySegment<Segment> package)
|
||||
{
|
||||
package = default;
|
||||
lock (rcv_queueLock)
|
||||
{
|
||||
var peekSize = -1;
|
||||
if (rcv_queue.Count == 0)
|
||||
{
|
||||
///没有可用包
|
||||
return -1;
|
||||
}
|
||||
|
||||
var seq = rcv_queue[0];
|
||||
|
||||
if (seq.frg == 0)
|
||||
{
|
||||
peekSize = (int)seq.len;
|
||||
}
|
||||
|
||||
if (rcv_queue.Count < seq.frg + 1)
|
||||
{
|
||||
///没有足够的包
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint length = 0;
|
||||
|
||||
Segment[] kcpSegments = ArrayPool<Segment>.Shared.Rent(seq.frg + 1);
|
||||
|
||||
var index = 0;
|
||||
foreach (var item in rcv_queue)
|
||||
{
|
||||
kcpSegments[index] = item;
|
||||
index++;
|
||||
length += item.len;
|
||||
if (item.frg == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
rcv_queue.RemoveRange(0, index);
|
||||
}
|
||||
|
||||
package = new ArraySegment<Segment>(kcpSegments, 0, index);
|
||||
|
||||
peekSize = (int)length;
|
||||
|
||||
if (peekSize <= 0)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
return peekSize;
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask RecvAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var arraySegment = await recvSignal.ReadAsync().ConfigureAwait(false);
|
||||
for (int i = arraySegment.Offset; i < arraySegment.Count; i++)
|
||||
{
|
||||
WriteRecv(writer, arraySegment.Array[i]);
|
||||
}
|
||||
|
||||
ArrayPool<Segment>.Shared.Return(arraySegment.Array, true);
|
||||
}
|
||||
|
||||
private void WriteRecv(IBufferWriter<byte> writer, Segment seg)
|
||||
{
|
||||
var curCount = (int)seg.len;
|
||||
var target = writer.GetSpan(curCount);
|
||||
seg.data.CopyTo(target);
|
||||
SegmentManager.Free(seg);
|
||||
writer.Advance(curCount);
|
||||
}
|
||||
|
||||
public async UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null)
|
||||
{
|
||||
var arraySegment = await recvSignal.ReadAsync().ConfigureAwait(false);
|
||||
int start = buffer.Offset;
|
||||
for (int i = arraySegment.Offset; i < arraySegment.Count; i++)
|
||||
{
|
||||
var target = new Memory<byte>(buffer.Array, start, buffer.Array.Length - start);
|
||||
|
||||
var seg = arraySegment.Array[i];
|
||||
seg.data.CopyTo(target.Span);
|
||||
start += seg.data.Length;
|
||||
|
||||
SegmentManager.Free(seg);
|
||||
}
|
||||
|
||||
ArrayPool<Segment>.Shared.Return(arraySegment.Array, true);
|
||||
return start - buffer.Offset;
|
||||
}
|
||||
|
||||
public async UniTask OutputAsync(IBufferWriter<byte> writer, object options = null)
|
||||
{
|
||||
var (Owner, Count) = await outq.ReadAsync().ConfigureAwait(false);
|
||||
WriteOut(writer, Owner, Count);
|
||||
}
|
||||
|
||||
private static void WriteOut(IBufferWriter<byte> writer, BufferOwner Owner, int Count)
|
||||
{
|
||||
var target = writer.GetSpan(Count);
|
||||
Owner.Memory.Span.Slice(0, Count).CopyTo(target);
|
||||
writer.Advance(Count);
|
||||
Owner.Dispose();
|
||||
}
|
||||
|
||||
protected internal override BufferOwner CreateBuffer(int needSize)
|
||||
{
|
||||
return MemoryPool<byte>.Shared.Rent(needSize);
|
||||
}
|
||||
|
||||
internal class OutputQ : QueuePipe<(BufferOwner Owner, int Count)>,
|
||||
IKcpCallback
|
||||
{
|
||||
public void Output(BufferOwner buffer, int avalidLength)
|
||||
{
|
||||
Write((buffer, avalidLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c30d30fa46372948b60bd56eec47fe6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,50 +0,0 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public abstract class KcpOutputWriter : IKcpOutputWriter
|
||||
{
|
||||
public int UnflushedBytes { get; set; }
|
||||
public IMemoryOwner<byte> MemoryOwner { get; set; }
|
||||
public void Flush()
|
||||
{
|
||||
Output(MemoryOwner, UnflushedBytes);
|
||||
MemoryOwner = null;
|
||||
UnflushedBytes = 0;
|
||||
}
|
||||
|
||||
public void Advance(int count)
|
||||
{
|
||||
UnflushedBytes += count;
|
||||
}
|
||||
|
||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
||||
{
|
||||
if (MemoryOwner == null)
|
||||
{
|
||||
MemoryOwner = MemoryPool<byte>.Shared.Rent(2048);
|
||||
}
|
||||
return MemoryOwner.Memory.Slice(UnflushedBytes);
|
||||
}
|
||||
|
||||
public Span<byte> GetSpan(int sizeHint = 0)
|
||||
{
|
||||
if (MemoryOwner == null)
|
||||
{
|
||||
MemoryOwner = MemoryPool<byte>.Shared.Rent(2048);
|
||||
}
|
||||
return MemoryOwner.Memory.Span.Slice(UnflushedBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Socket发送是要pin byte[],为了不阻塞KcpFlush,动态缓存是必须的。
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="avalidLength"></param>
|
||||
public abstract void Output(IMemoryOwner<byte> buffer, int avalidLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a058d8fa8a92dc94395bfd6e1f6fdb8f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,402 +0,0 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// 调整了没存布局,直接拷贝块提升性能。
|
||||
/// <para>结构体保存内容只有一个指针,不用担心参数传递过程中的性能</para>
|
||||
/// https://github.com/skywind3000/kcp/issues/118#issuecomment-338133930
|
||||
/// <para>不要对没有初始化的KcpSegment(内部指针为0,所有属性都将指向位置区域) 进行任何赋值操作,可能导致内存损坏。
|
||||
/// 出于性能考虑,没有对此项进行安全检查。</para>
|
||||
/// </summary>
|
||||
public struct KcpSegment : IKcpSegment
|
||||
{
|
||||
internal readonly unsafe byte* ptr;
|
||||
public unsafe KcpSegment(byte* intPtr, uint appendDateSize)
|
||||
{
|
||||
this.ptr = intPtr;
|
||||
len = appendDateSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用完必须显示释放,否则内存泄漏
|
||||
/// </summary>
|
||||
/// <param name="appendDateSize"></param>
|
||||
/// <returns></returns>
|
||||
public static KcpSegment AllocHGlobal(int appendDateSize)
|
||||
{
|
||||
var total = LocalOffset + HeadOffset + appendDateSize;
|
||||
IntPtr intPtr = Marshal.AllocHGlobal(total);
|
||||
unsafe
|
||||
{
|
||||
///清零 不知道是不是有更快的清0方法?
|
||||
Span<byte> span = new Span<byte>(intPtr.ToPointer(), total);
|
||||
span.Clear();
|
||||
|
||||
return new KcpSegment((byte*)intPtr.ToPointer(), (uint)appendDateSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放非托管内存
|
||||
/// </summary>
|
||||
/// <param name="seg"></param>
|
||||
public static void FreeHGlobal(KcpSegment seg)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Marshal.FreeHGlobal((IntPtr)seg.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// 以下为本机使用的参数
|
||||
/// <summary>
|
||||
/// offset = 0
|
||||
/// </summary>
|
||||
public uint resendts
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 0);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 0) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = 4
|
||||
/// </summary>
|
||||
public uint rto
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 4);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 4) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = 8
|
||||
/// </summary>
|
||||
public uint fastack
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 8);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 8) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = 12
|
||||
/// </summary>
|
||||
public uint xmit
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(ptr + 12);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(ptr + 12) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///以下为需要网络传输的参数
|
||||
public const int LocalOffset = 4 * 4;
|
||||
public const int HeadOffset = KcpConst.IKCP_OVERHEAD;
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/>
|
||||
/// </summary>
|
||||
/// https://github.com/skywind3000/kcp/issues/134
|
||||
public uint conv
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 0 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 0 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 4
|
||||
/// </summary>
|
||||
public byte cmd
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(LocalOffset + 4 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(LocalOffset + 4 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 5
|
||||
/// </summary>
|
||||
public byte frg
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(LocalOffset + 5 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(LocalOffset + 5 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 6
|
||||
/// </summary>
|
||||
public ushort wnd
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(ushort*)(LocalOffset + 6 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(ushort*)(LocalOffset + 6 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 8
|
||||
/// </summary>
|
||||
public uint ts
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 8 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 8 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> SendNumber? </para>
|
||||
/// offset = <see cref="LocalOffset"/> + 12
|
||||
/// </summary>
|
||||
public uint sn
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 12 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 12 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// offset = <see cref="LocalOffset"/> + 16
|
||||
/// </summary>
|
||||
public uint una
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 16 + ptr);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 16 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> AppendDateSize </para>
|
||||
/// offset = <see cref="LocalOffset"/> + 20
|
||||
/// </summary>
|
||||
public uint len
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return *(uint*)(LocalOffset + 20 + ptr);
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
*(uint*)(LocalOffset + 20 + ptr) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// https://github.com/skywind3000/kcp/issues/35#issuecomment-263770736
|
||||
public Span<byte> data
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Span<byte>(LocalOffset + HeadOffset + ptr, (int)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将片段中的要发送的数据拷贝到指定缓冲区
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
public int Encode(Span<byte> buffer)
|
||||
{
|
||||
var datelen = (int)(HeadOffset + len);
|
||||
|
||||
///备用偏移值 现阶段没有使用
|
||||
const int offset = 0;
|
||||
|
||||
if (KcpConst.IsLittleEndian)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
///小端可以一次拷贝
|
||||
unsafe
|
||||
{
|
||||
///要发送的数据从LocalOffset开始。
|
||||
///本结构体调整了要发送字段和单机使用字段的位置,让报头数据和数据连续,节约一次拷贝。
|
||||
Span<byte> sendDate = new Span<byte>(ptr + LocalOffset, datelen);
|
||||
sendDate.CopyTo(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
///大端可以一次拷贝
|
||||
unsafe
|
||||
{
|
||||
///要发送的数据从LocalOffset开始。
|
||||
///本结构体调整了要发送字段和单机使用字段的位置,让报头数据和数据连续,节约一次拷贝。
|
||||
Span<byte> sendDate = new Span<byte>(ptr + LocalOffset, datelen);
|
||||
sendDate.CopyTo(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return datelen;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daab08fc1f00355429c23a1e36993942
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public partial class KcpCore<Segment>
|
||||
{
|
||||
public KcpLogMask LogMask { get; set; } = KcpLogMask.IKCP_LOG_PARSE_DATA | KcpLogMask.IKCP_LOG_NEED_SEND | KcpLogMask.IKCP_LOG_DEAD_LINK;
|
||||
|
||||
public virtual bool CanLog(KcpLogMask mask)
|
||||
{
|
||||
if ((mask & LogMask) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
if (TraceListener != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
public System.Diagnostics.TraceListener TraceListener { get; set; }
|
||||
#endif
|
||||
|
||||
public virtual void LogFail(string message)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
TraceListener?.Fail(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual void LogWriteLine(string message, string category)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
TraceListener?.WriteLine(message, category);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Obsolete("一定要先判断CanLog 内部判断是否存在TraceListener,避免在没有TraceListener时生成字符串", true)]
|
||||
public virtual void LogWriteLine(string message, KcpLogMask mask)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
if (CanLog(mask))
|
||||
{
|
||||
LogWriteLine(message, mask.ToString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum KcpLogMask
|
||||
{
|
||||
IKCP_LOG_OUTPUT = 1 << 0,
|
||||
IKCP_LOG_INPUT = 1 << 1,
|
||||
IKCP_LOG_SEND = 1 << 2,
|
||||
IKCP_LOG_RECV = 1 << 3,
|
||||
IKCP_LOG_IN_DATA = 1 << 4,
|
||||
IKCP_LOG_IN_ACK = 1 << 5,
|
||||
IKCP_LOG_IN_PROBE = 1 << 6,
|
||||
IKCP_LOG_IN_WINS = 1 << 7,
|
||||
IKCP_LOG_OUT_DATA = 1 << 8,
|
||||
IKCP_LOG_OUT_ACK = 1 << 9,
|
||||
IKCP_LOG_OUT_PROBE = 1 << 10,
|
||||
IKCP_LOG_OUT_WINS = 1 << 11,
|
||||
|
||||
IKCP_LOG_PARSE_DATA = 1 << 12,
|
||||
IKCP_LOG_NEED_SEND = 1 << 13,
|
||||
IKCP_LOG_DEAD_LINK = 1 << 14,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39791b0c45bf4864e87385c477d9b50c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,265 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
/// <summary>
|
||||
/// 动态申请非托管内存
|
||||
/// </summary>
|
||||
public class SimpleSegManager : ISegmentManager<KcpSegment>
|
||||
{
|
||||
public static SimpleSegManager Default { get; } = new SimpleSegManager();
|
||||
public KcpSegment Alloc(int appendDateSize)
|
||||
{
|
||||
return KcpSegment.AllocHGlobal(appendDateSize);
|
||||
}
|
||||
|
||||
public void Free(KcpSegment seg)
|
||||
{
|
||||
KcpSegment.FreeHGlobal(seg);
|
||||
}
|
||||
|
||||
public class Kcp : Kcp<KcpSegment>
|
||||
{
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_, callback, rentable)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO : KcpIO<KcpSegment>
|
||||
{
|
||||
public KcpIO(uint conv_)
|
||||
: base(conv_)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 申请固定大小非托管内存。使用这个就不能SetMtu了,大小已经写死。
|
||||
/// </summary>
|
||||
/// <remarks>需要大量测试</remarks>
|
||||
public unsafe class UnSafeSegManager : ISegmentManager<KcpSegment>
|
||||
{
|
||||
public static UnSafeSegManager Default { get; } = new UnSafeSegManager();
|
||||
/// <summary>
|
||||
/// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。
|
||||
/// </summary>
|
||||
public const int blockSize = 512 * 3;
|
||||
public HashSet<IntPtr> header = new HashSet<IntPtr>();
|
||||
public Stack<IntPtr> blocks = new Stack<IntPtr>();
|
||||
public readonly object locker = new object();
|
||||
public UnSafeSegManager()
|
||||
{
|
||||
Alloc();
|
||||
}
|
||||
|
||||
void Alloc()
|
||||
{
|
||||
int count = 50;
|
||||
IntPtr intPtr = Marshal.AllocHGlobal(blockSize * count);
|
||||
header.Add(intPtr);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
blocks.Push(intPtr + blockSize * i);
|
||||
}
|
||||
}
|
||||
|
||||
~UnSafeSegManager()
|
||||
{
|
||||
foreach (var item in header)
|
||||
{
|
||||
Marshal.FreeHGlobal(item);
|
||||
}
|
||||
}
|
||||
|
||||
public KcpSegment Alloc(int appendDateSize)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
var total = KcpSegment.LocalOffset + KcpSegment.HeadOffset + appendDateSize;
|
||||
if (total > blockSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (blocks.Count > 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Alloc();
|
||||
}
|
||||
|
||||
var ptr = blocks.Pop();
|
||||
Span<byte> span = new Span<byte>(ptr.ToPointer(), blockSize);
|
||||
span.Clear();
|
||||
return new KcpSegment((byte*)ptr.ToPointer(), (uint)appendDateSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void Free(KcpSegment seg)
|
||||
{
|
||||
IntPtr ptr = (IntPtr)seg.ptr;
|
||||
blocks.Push(ptr);
|
||||
}
|
||||
|
||||
public class Kcp : Kcp<KcpSegment>
|
||||
{
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_, callback, rentable)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO : KcpIO<KcpSegment>
|
||||
{
|
||||
public KcpIO(uint conv_)
|
||||
: base(conv_)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 使用内存池,而不是非托管内存,有内存alloc,但是不多。可以解决Marshal.AllocHGlobal 内核调用带来的性能问题
|
||||
/// </summary>
|
||||
public class PoolSegManager : ISegmentManager<PoolSegManager.Seg>
|
||||
{
|
||||
public static PoolSegManager Default { get; } = new PoolSegManager();
|
||||
|
||||
/// <summary>
|
||||
/// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。
|
||||
/// </summary>
|
||||
public const int blockSize = 512 * 3;
|
||||
public class Seg : IKcpSegment
|
||||
{
|
||||
byte[] cache;
|
||||
public Seg(int blockSize)
|
||||
{
|
||||
cache = Buffers.ArrayPool<byte>.Shared.Rent(blockSize);
|
||||
}
|
||||
|
||||
///以下为需要网络传输的参数
|
||||
public const int LocalOffset = 4 * 4;
|
||||
public const int HeadOffset = Kcp.IKCP_OVERHEAD;
|
||||
|
||||
public byte cmd { get; set; }
|
||||
public uint conv { get; set; }
|
||||
public Span<byte> data => cache.AsSpan().Slice(0, (int)len);
|
||||
public uint fastack { get; set; }
|
||||
public byte frg { get; set; }
|
||||
public uint len { get; internal set; }
|
||||
public uint resendts { get; set; }
|
||||
public uint rto { get; set; }
|
||||
public uint sn { get; set; }
|
||||
public uint ts { get; set; }
|
||||
public uint una { get; set; }
|
||||
public ushort wnd { get; set; }
|
||||
public uint xmit { get; set; }
|
||||
|
||||
public int Encode(Span<byte> buffer)
|
||||
{
|
||||
var datelen = (int)(HeadOffset + len);
|
||||
|
||||
///备用偏移值 现阶段没有使用
|
||||
const int offset = 0;
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv);
|
||||
buffer[offset + 4] = cmd;
|
||||
buffer[offset + 5] = frg;
|
||||
BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd);
|
||||
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len);
|
||||
|
||||
data.CopyTo(buffer.Slice(HeadOffset));
|
||||
}
|
||||
|
||||
return datelen;
|
||||
}
|
||||
}
|
||||
ConcurrentStack<Seg> Pool = new ConcurrentStack<Seg>();
|
||||
public Seg Alloc(int appendDateSize)
|
||||
{
|
||||
if (appendDateSize > blockSize)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (Pool.TryPop(out var ret))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = new Seg(blockSize);
|
||||
}
|
||||
ret.len = (uint)appendDateSize;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Free(Seg seg)
|
||||
{
|
||||
seg.cmd = 0;
|
||||
seg.conv = 0;
|
||||
seg.fastack = 0;
|
||||
seg.frg = 0;
|
||||
seg.len = 0;
|
||||
seg.resendts = 0;
|
||||
seg.rto = 0;
|
||||
seg.sn = 0;
|
||||
seg.ts = 0;
|
||||
seg.una = 0;
|
||||
seg.wnd = 0;
|
||||
seg.xmit = 0;
|
||||
Pool.Push(seg);
|
||||
}
|
||||
|
||||
public class Kcp : Kcp<Seg>
|
||||
{
|
||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
||||
: base(conv_, callback, rentable)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
|
||||
public class KcpIO : KcpIO<Seg>
|
||||
{
|
||||
public KcpIO(uint conv_)
|
||||
: base(conv_)
|
||||
{
|
||||
SegmentManager = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7f0d71aa1362b6488c3173d525fd284
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,65 +0,0 @@
|
||||
using System.Buffers;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp.Simple
|
||||
{
|
||||
/// <summary>
|
||||
/// 简单例子。
|
||||
/// </summary>
|
||||
public class SimpleKcpClient : IKcpCallback
|
||||
{
|
||||
UdpClient client;
|
||||
|
||||
public SimpleKcpClient(int port)
|
||||
: this(port, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SimpleKcpClient(int port, IPEndPoint endPoint)
|
||||
{
|
||||
client = new UdpClient(port);
|
||||
kcp = new SimpleSegManager.Kcp(2001, this);
|
||||
this.EndPoint = endPoint;
|
||||
BeginRecv();
|
||||
}
|
||||
|
||||
public SimpleSegManager.Kcp kcp { get; }
|
||||
public IPEndPoint EndPoint { get; set; }
|
||||
|
||||
public void Output(IMemoryOwner<byte> buffer, int avalidLength)
|
||||
{
|
||||
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
|
||||
client.SendAsync(s, s.Length, EndPoint);
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
public void SendAsync(byte[] datagram, int bytes)
|
||||
{
|
||||
kcp.Send(datagram.AsSpan().Slice(0, bytes));
|
||||
}
|
||||
|
||||
public async UniTask<byte[]> ReceiveAsync()
|
||||
{
|
||||
var (buffer, avalidLength) = kcp.TryRecv();
|
||||
while (buffer == null)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
(buffer, avalidLength) = kcp.TryRecv();
|
||||
}
|
||||
|
||||
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
|
||||
return s;
|
||||
}
|
||||
|
||||
private async void BeginRecv()
|
||||
{
|
||||
var res = await client.ReceiveAsync();
|
||||
EndPoint = res.RemoteEndPoint;
|
||||
kcp.Input(res.Buffer);
|
||||
BeginRecv();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff16ed9b89525df4aa6501c0edc679d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,48 +0,0 @@
|
||||
using System.Net.Sockets.Kcp.Simple;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
namespace TestServer
|
||||
{
|
||||
/// <summary>
|
||||
/// 简单例子。
|
||||
/// </summary>
|
||||
class SimpleKcpServer
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
|
||||
SimpleKcpClient kcpClient = new SimpleKcpClient(40001);
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
kcpClient.kcp.Update(DateTimeOffset.UtcNow);
|
||||
await Task.Delay(10);
|
||||
}
|
||||
});
|
||||
|
||||
StartRecv(kcpClient);
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
static async void StartRecv(SimpleKcpClient client)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var res = await client.ReceiveAsync();
|
||||
var str = System.Text.Encoding.UTF8.GetString(res);
|
||||
if ("发送一条消息" == str)
|
||||
{
|
||||
Console.WriteLine(str);
|
||||
|
||||
var buffer = System.Text.Encoding.UTF8.GetBytes("回复一条消息");
|
||||
client.SendAsync(buffer, buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97e221b7388e412da59b0f4d200cb891
|
||||
timeCreated: 1682095200
|
@@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
//[assembly: InternalsVisibleTo("UnitTestProject1")]
|
||||
|
||||
namespace System.Net.Sockets.Kcp
|
||||
{
|
||||
public static class KcpExtension_FDF71D0BC31D49C48EEA8FAA51F017D4
|
||||
{
|
||||
private static readonly DateTime utc_time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
[Obsolete("", true)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTime(this in DateTime time)
|
||||
{
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc_time).TotalMilliseconds) & 0xffffffff);
|
||||
}
|
||||
|
||||
private static readonly DateTimeOffset utc1970 = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTimeOld(this in DateTimeOffset time)
|
||||
{
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTime2(this in DateTimeOffset time)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
return (uint)(time.ToUnixTimeMilliseconds() & 0xffffffff);
|
||||
#else
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ConvertTime(this in DateTimeOffset time)
|
||||
{
|
||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
||||
return (uint)(time.ToUnixTimeMilliseconds());
|
||||
#else
|
||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ToLogString<T>(this T segment, bool local = false)
|
||||
where T : IKcpSegment
|
||||
{
|
||||
if (local)
|
||||
{
|
||||
return $"sn:{segment.sn,2} una:{segment.una,2} frg:{segment.frg,2} cmd:{segment.cmd,2} len:{segment.len,2} wnd:{segment.wnd} [ LocalValue: xmit:{segment.xmit} fastack:{segment.fastack} rto:{segment.rto} ]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"sn:{segment.sn,2} una:{segment.una,2} frg:{segment.frg,2} cmd:{segment.cmd,2} len:{segment.len,2} wnd:{segment.wnd}";
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Encode<T>(this T Seg, IBufferWriter<byte> writer)
|
||||
where T : IKcpSegment
|
||||
{
|
||||
var totalLength = (int)(KcpSegment.HeadOffset + Seg.len);
|
||||
var span = writer.GetSpan(totalLength);
|
||||
Seg.Encode(span);
|
||||
writer.Advance(totalLength);
|
||||
return totalLength;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd298c4aa310a7e448c4694ddc41337e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,287 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
internal sealed partial class NetworkManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Kcp 网络频道。
|
||||
/// </summary>
|
||||
private sealed class KcpNetworkChannel : NetworkChannelBase
|
||||
{
|
||||
private readonly AsyncCallback _connectCallback;
|
||||
private readonly AsyncCallback _sendCallback;
|
||||
private readonly AsyncCallback _receiveCallback;
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络服务类型。
|
||||
/// </summary>
|
||||
public override ServiceType ServiceType => ServiceType.Kcp;
|
||||
|
||||
|
||||
public KcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
|
||||
: base(name, networkChannelHelper)
|
||||
{
|
||||
_connectCallback = ConnectCallback;
|
||||
_sendCallback = SendCallback;
|
||||
_receiveCallback = ReceiveCallback;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接到远程主机。
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
||||
/// <param name="port">远程主机的端口号。</param>
|
||||
/// <param name="userData">用户自定义数据。</param>
|
||||
public override void Connect(IPAddress ipAddress, int port, object userData)
|
||||
{
|
||||
base.Connect(ipAddress, port, userData);
|
||||
MSocket = new Socket(ipAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||
if (MSocket == null)
|
||||
{
|
||||
string errorMessage = "Initialize network channel failure.";
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new GameFrameworkException(errorMessage);
|
||||
}
|
||||
|
||||
NetworkChannelHelper.PrepareForConnecting();
|
||||
ConnectAsync(ipAddress, port, userData);
|
||||
}
|
||||
|
||||
private void ConnectAsync(IPAddress ipAddress, int port, object userData)
|
||||
{
|
||||
try
|
||||
{
|
||||
MSocket.BeginConnect(ipAddress, port, _connectCallback, new ConnectState(MSocket, userData));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
SocketException socketException = exception as SocketException;
|
||||
NetworkChannelError(this, NetworkErrorCode.ConnectError,
|
||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
||||
exception.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ProcessSend()
|
||||
{
|
||||
if (base.ProcessSend())
|
||||
{
|
||||
SendAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ConnectCallback(IAsyncResult ar)
|
||||
{
|
||||
ConnectState socketUserData = (ConnectState)ar.AsyncState;
|
||||
try
|
||||
{
|
||||
socketUserData.Socket.EndConnect(ar);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Active = false;
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
SocketException socketException = exception as SocketException;
|
||||
NetworkChannelError(this, NetworkErrorCode.ConnectError,
|
||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
||||
exception.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
MSentPacketCount = 0;
|
||||
MReceivedPacketCount = 0;
|
||||
|
||||
lock (SendPacketPool)
|
||||
{
|
||||
SendPacketPool.Clear();
|
||||
}
|
||||
|
||||
lock (MHeartBeatState)
|
||||
{
|
||||
MHeartBeatState.Reset(true);
|
||||
}
|
||||
|
||||
if (NetworkChannelConnected != null)
|
||||
{
|
||||
NetworkChannelConnected(this, socketUserData.UserData);
|
||||
}
|
||||
|
||||
Active = true;
|
||||
ReceiveAsync();
|
||||
}
|
||||
|
||||
private void SendAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
MSocket.BeginSend(MSendState.Stream.GetBuffer(), (int)MSendState.Stream.Position,
|
||||
(int)(MSendState.Stream.Length - MSendState.Stream.Position), SocketFlags.None, _sendCallback,
|
||||
MSocket);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Active = false;
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
SocketException socketException = exception as SocketException;
|
||||
NetworkChannelError(this, NetworkErrorCode.SendError,
|
||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
||||
exception.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendCallback(IAsyncResult ar)
|
||||
{
|
||||
Socket socket = (Socket)ar.AsyncState;
|
||||
if (!socket.Connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int bytesSent = 0;
|
||||
try
|
||||
{
|
||||
bytesSent = socket.EndSend(ar);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Active = false;
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
SocketException socketException = exception as SocketException;
|
||||
NetworkChannelError(this, NetworkErrorCode.SendError,
|
||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
||||
exception.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
MSendState.Stream.Position += bytesSent;
|
||||
if (MSendState.Stream.Position < MSendState.Stream.Length)
|
||||
{
|
||||
SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
MSentPacketCount++;
|
||||
MSendState.Reset();
|
||||
}
|
||||
|
||||
private void ReceiveAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
MSocket.BeginReceive(MReceiveState.Stream.GetBuffer(), (int)MReceiveState.Stream.Position,
|
||||
(int)(MReceiveState.Stream.Length - MReceiveState.Stream.Position), SocketFlags.None,
|
||||
_receiveCallback, MSocket);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Active = false;
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
SocketException socketException = exception as SocketException;
|
||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError,
|
||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
||||
exception.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceiveCallback(IAsyncResult ar)
|
||||
{
|
||||
Socket socket = (Socket)ar.AsyncState;
|
||||
if (!socket.Connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int bytesReceived = 0;
|
||||
try
|
||||
{
|
||||
bytesReceived = socket.EndReceive(ar);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Active = false;
|
||||
if (NetworkChannelError != null)
|
||||
{
|
||||
SocketException socketException = exception as SocketException;
|
||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError,
|
||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
||||
exception.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
if (bytesReceived <= 0)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
MReceiveState.Stream.Position += bytesReceived;
|
||||
if (MReceiveState.Stream.Position < MReceiveState.Stream.Length)
|
||||
{
|
||||
ReceiveAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
MReceiveState.Stream.Position = 0L;
|
||||
|
||||
bool processSuccess = false;
|
||||
if (MReceiveState.PacketHeader != null)
|
||||
{
|
||||
processSuccess = ProcessPacket();
|
||||
MReceivedPacketCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
processSuccess = ProcessPacketHeader();
|
||||
}
|
||||
|
||||
if (processSuccess)
|
||||
{
|
||||
ReceiveAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70ff4fca885c401ea776223622deab75
|
||||
timeCreated: 1682092071
|
@@ -1,54 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
public class NetUtil
|
||||
{
|
||||
public static bool IsHaveIpV6Address(IPAddress[] ipAddresses, ref IPAddress[] outIPs)
|
||||
{
|
||||
int v6Count = 0;
|
||||
for (int i = 0; i < ipAddresses.Length; i++)
|
||||
{
|
||||
if (System.Net.Sockets.AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily))
|
||||
{
|
||||
v6Count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (v6Count > 0)
|
||||
{
|
||||
outIPs = new IPAddress[v6Count];
|
||||
int resIndex = 0;
|
||||
for (int i = 0; i < ipAddresses.Length; i++)
|
||||
{
|
||||
if (System.Net.Sockets.AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily))
|
||||
{
|
||||
outIPs[resIndex++] = ipAddresses[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IPEndPoint GetEndPoint(string server, int port)
|
||||
{
|
||||
IPAddress[] ps = Dns.GetHostAddresses(server);
|
||||
IPAddress[] finalIps = ps;
|
||||
if (Socket.OSSupportsIPv6 && NetUtil.IsHaveIpV6Address(ps, ref finalIps))
|
||||
{
|
||||
Log.Error("socket use addr ipV6: {0}, IP count:{1} AddressFamily[{2}]", server, finalIps.Length, finalIps[0].AddressFamily);
|
||||
}
|
||||
|
||||
if (finalIps.Length > 0)
|
||||
{
|
||||
return new IPEndPoint(finalIps[0], port);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26846cca9e0f4d3c965d58882c1b503e
|
||||
timeCreated: 1681991073
|
@@ -1,144 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using GameBase;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络组件。
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class Network : UnitySingleton<Network>
|
||||
{
|
||||
private NetworkManager m_NetworkManager = null;
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络频道数量。
|
||||
/// </summary>
|
||||
public int NetworkChannelCount => m_NetworkManager.NetworkChannelCount;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏框架组件初始化。
|
||||
/// </summary>
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
// m_NetworkManager = GameFrameworkEntry.GetModule<INetworkManager>();
|
||||
m_NetworkManager = new NetworkManager();
|
||||
if (m_NetworkManager == null)
|
||||
{
|
||||
Log.Fatal("Network manager is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_NetworkManager.NetworkConnected += OnNetworkConnected;
|
||||
m_NetworkManager.NetworkClosed += OnNetworkClosed;
|
||||
m_NetworkManager.NetworkMissHeartBeat += OnNetworkMissHeartBeat;
|
||||
m_NetworkManager.NetworkError += OnNetworkError;
|
||||
m_NetworkManager.NetworkCustomError += OnNetworkCustomError;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
m_NetworkManager.Update(GameTime.deltaTime, GameTime.unscaledDeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否存在网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <returns>是否存在网络频道。</returns>
|
||||
public bool HasNetworkChannel(string name)
|
||||
{
|
||||
return m_NetworkManager.HasNetworkChannel(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <returns>要获取的网络频道。</returns>
|
||||
public INetworkChannel GetNetworkChannel(string name)
|
||||
{
|
||||
return m_NetworkManager.GetNetworkChannel(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络频道。
|
||||
/// </summary>
|
||||
/// <returns>所有网络频道。</returns>
|
||||
public INetworkChannel[] StaticGetAllNetworkChannels()
|
||||
{
|
||||
return m_NetworkManager.GetAllNetworkChannels();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络频道。
|
||||
/// </summary>
|
||||
/// <returns>所有网络频道。</returns>
|
||||
public INetworkChannel[] GetAllNetworkChannels()
|
||||
{
|
||||
return m_NetworkManager.GetAllNetworkChannels();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有网络频道。
|
||||
/// </summary>
|
||||
/// <param name="results">所有网络频道。</param>
|
||||
public void GetAllNetworkChannels(List<INetworkChannel> results)
|
||||
{
|
||||
m_NetworkManager.GetAllNetworkChannels(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建网络频道。
|
||||
/// </summary>
|
||||
/// <param name="name">网络频道名称。</param>
|
||||
/// <param name="serviceType">网络服务类型。</param>
|
||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
||||
/// <returns>要创建的网络频道。</returns>
|
||||
public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType,
|
||||
INetworkChannelHelper networkChannelHelper)
|
||||
{
|
||||
return m_NetworkManager.CreateNetworkChannel(name, serviceType, networkChannelHelper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 销毁网络频道。
|
||||
/// </summary>
|
||||
/// <param name="channelName">网络频道名称。</param>
|
||||
/// <returns>是否销毁网络频道成功。</returns>
|
||||
public bool DestroyNetworkChannel(string channelName)
|
||||
{
|
||||
return m_NetworkManager.DestroyNetworkChannel(channelName);
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(INetworkChannel channel, object userdata)
|
||||
{
|
||||
GameEvent.Send(NetworkEvent.NetworkConnectedEvent, channel, userdata);
|
||||
}
|
||||
|
||||
private void OnNetworkClosed(INetworkChannel channel)
|
||||
{
|
||||
GameEvent.Send(NetworkEvent.NetworkClosedEvent, channel);
|
||||
}
|
||||
|
||||
private void OnNetworkMissHeartBeat(INetworkChannel channel, int missCount)
|
||||
{
|
||||
GameEvent.Send(NetworkEvent.NetworkMissHeartBeatEvent, channel, missCount);
|
||||
}
|
||||
|
||||
private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError,
|
||||
string errorMessage)
|
||||
{
|
||||
GameEvent.Send(NetworkEvent.NetworkErrorEvent, channel, networkErrorCode, socketError, errorMessage);
|
||||
}
|
||||
|
||||
private void OnNetworkCustomError(INetworkChannel channel, object userData)
|
||||
{
|
||||
GameEvent.Send(NetworkEvent.NetworkCustomErrorEvent, channel, userData);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8edbc7d090fd4bb1ad2c6aa967ff0b76
|
||||
timeCreated: 1682044812
|
@@ -1,53 +0,0 @@
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络错误码。
|
||||
/// </summary>
|
||||
public enum NetworkErrorCode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 未知错误。
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 地址族错误。
|
||||
/// </summary>
|
||||
AddressFamilyError,
|
||||
|
||||
/// <summary>
|
||||
/// Socket 错误。
|
||||
/// </summary>
|
||||
SocketError,
|
||||
|
||||
/// <summary>
|
||||
/// 连接错误。
|
||||
/// </summary>
|
||||
ConnectError,
|
||||
|
||||
/// <summary>
|
||||
/// 发送错误。
|
||||
/// </summary>
|
||||
SendError,
|
||||
|
||||
/// <summary>
|
||||
/// 接收错误。
|
||||
/// </summary>
|
||||
ReceiveError,
|
||||
|
||||
/// <summary>
|
||||
/// 序列化错误。
|
||||
/// </summary>
|
||||
SerializeError,
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包头错误。
|
||||
/// </summary>
|
||||
DeserializePacketHeaderError,
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包错误。
|
||||
/// </summary>
|
||||
DeserializePacketError
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bb8ca2ece7d49d28b1b4fd1bfa027e6
|
||||
timeCreated: 1681993877
|
@@ -1,30 +0,0 @@
|
||||
namespace TEngine
|
||||
{
|
||||
public class NetworkEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 网网络连接成功事件。
|
||||
/// </summary>
|
||||
public static int NetworkConnectedEvent = StringId.StringToHash("NetworkEvent.NetworkConnectedEvent");
|
||||
|
||||
/// <summary>
|
||||
/// 网络连接关闭事件。
|
||||
/// </summary>
|
||||
public static int NetworkClosedEvent = StringId.StringToHash("NetworkEvent.NetworkClosedEvent");
|
||||
|
||||
/// <summary>
|
||||
/// 网络错误事件。
|
||||
/// </summary>
|
||||
public static int NetworkErrorEvent = StringId.StringToHash("NetworkEvent.NetworkErrorEvent");
|
||||
|
||||
/// <summary>
|
||||
/// 用户自定义网络错误事件。
|
||||
/// </summary>
|
||||
public static int NetworkCustomErrorEvent = StringId.StringToHash("NetworkEvent.NetworkCustomErrorEvent");
|
||||
|
||||
/// <summary>
|
||||
/// 网络心跳包丢失事件。
|
||||
/// </summary>
|
||||
public static int NetworkMissHeartBeatEvent = StringId.StringToHash("NetworkEvent.NetworkMissHeartBeatEvent");
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e3b5eb5b76244498181c6117f0e0b1a
|
||||
timeCreated: 1681993978
|
@@ -1,23 +0,0 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
internal sealed partial class NetworkManager
|
||||
{
|
||||
private sealed class ConnectState
|
||||
{
|
||||
private readonly Socket _socket;
|
||||
private readonly object _userData;
|
||||
|
||||
public ConnectState(Socket socket, object userData)
|
||||
{
|
||||
_socket = socket;
|
||||
_userData = userData;
|
||||
}
|
||||
|
||||
public Socket Socket => _socket;
|
||||
|
||||
public object UserData => _userData;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user