parent
c5cb5d3465
commit
b355fde29b
1168 changed files with 90539 additions and 0 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: e1ed6fe6cfaf5af468612fa52a287f3a |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 3e63c89f34a3fb94d882673dcce0bd76 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: be9e6e8cc5f6c814e88cb55ffb4ec10f |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
|
After Width: | Height: | Size: 694 KiB |
@ -0,0 +1,135 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: e4903b2f233b9f44c8b7737928023b34 |
||||||
|
TextureImporter: |
||||||
|
internalIDToNameTable: [] |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 12 |
||||||
|
mipmaps: |
||||||
|
mipMapMode: 0 |
||||||
|
enableMipMap: 1 |
||||||
|
sRGBTexture: 1 |
||||||
|
linearTexture: 0 |
||||||
|
fadeOut: 0 |
||||||
|
borderMipMap: 0 |
||||||
|
mipMapsPreserveCoverage: 0 |
||||||
|
alphaTestReferenceValue: 0.5 |
||||||
|
mipMapFadeDistanceStart: 1 |
||||||
|
mipMapFadeDistanceEnd: 3 |
||||||
|
bumpmap: |
||||||
|
convertToNormalMap: 0 |
||||||
|
externalNormalMap: 0 |
||||||
|
heightScale: 0.25 |
||||||
|
normalMapFilter: 0 |
||||||
|
isReadable: 0 |
||||||
|
streamingMipmaps: 0 |
||||||
|
streamingMipmapsPriority: 0 |
||||||
|
vTOnly: 0 |
||||||
|
ignoreMasterTextureLimit: 0 |
||||||
|
grayScaleToAlpha: 0 |
||||||
|
generateCubemap: 6 |
||||||
|
cubemapConvolution: 0 |
||||||
|
seamlessCubemap: 0 |
||||||
|
textureFormat: 1 |
||||||
|
maxTextureSize: 2048 |
||||||
|
textureSettings: |
||||||
|
serializedVersion: 2 |
||||||
|
filterMode: 1 |
||||||
|
aniso: 1 |
||||||
|
mipBias: 0 |
||||||
|
wrapU: 0 |
||||||
|
wrapV: 0 |
||||||
|
wrapW: 0 |
||||||
|
nPOTScale: 1 |
||||||
|
lightmap: 0 |
||||||
|
compressionQuality: 50 |
||||||
|
spriteMode: 0 |
||||||
|
spriteExtrude: 1 |
||||||
|
spriteMeshType: 1 |
||||||
|
alignment: 0 |
||||||
|
spritePivot: {x: 0.5, y: 0.5} |
||||||
|
spritePixelsToUnits: 100 |
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
||||||
|
spriteGenerateFallbackPhysicsShape: 1 |
||||||
|
alphaUsage: 1 |
||||||
|
alphaIsTransparency: 0 |
||||||
|
spriteTessellationDetail: -1 |
||||||
|
textureType: 0 |
||||||
|
textureShape: 1 |
||||||
|
singleChannelComponent: 0 |
||||||
|
flipbookRows: 1 |
||||||
|
flipbookColumns: 1 |
||||||
|
maxTextureSizeSet: 0 |
||||||
|
compressionQualitySet: 0 |
||||||
|
textureFormatSet: 0 |
||||||
|
ignorePngGamma: 0 |
||||||
|
applyGammaDecoding: 0 |
||||||
|
cookieLightType: 0 |
||||||
|
platformSettings: |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: DefaultTexturePlatform |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: Standalone |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: Server |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: Android |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
spriteSheet: |
||||||
|
serializedVersion: 2 |
||||||
|
sprites: [] |
||||||
|
outline: [] |
||||||
|
physicsShape: [] |
||||||
|
bones: [] |
||||||
|
spriteID: |
||||||
|
internalID: 0 |
||||||
|
vertices: [] |
||||||
|
indices: |
||||||
|
edges: [] |
||||||
|
weights: [] |
||||||
|
secondaryTextures: [] |
||||||
|
nameFileIdTable: {} |
||||||
|
spritePackingTag: |
||||||
|
pSDRemoveMatte: 0 |
||||||
|
pSDShowRemoveMatteOption: 0 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,82 @@ |
|||||||
|
%YAML 1.1 |
||||||
|
%TAG !u! tag:unity3d.com,2011: |
||||||
|
--- !u!21 &2100000 |
||||||
|
Material: |
||||||
|
serializedVersion: 8 |
||||||
|
m_ObjectHideFlags: 0 |
||||||
|
m_CorrespondingSourceObject: {fileID: 0} |
||||||
|
m_PrefabInstance: {fileID: 0} |
||||||
|
m_PrefabAsset: {fileID: 0} |
||||||
|
m_Name: vr_glove_color |
||||||
|
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} |
||||||
|
m_ValidKeywords: |
||||||
|
- _EMISSION |
||||||
|
- _NORMALMAP |
||||||
|
m_InvalidKeywords: [] |
||||||
|
m_LightmapFlags: 1 |
||||||
|
m_EnableInstancingVariants: 0 |
||||||
|
m_DoubleSidedGI: 0 |
||||||
|
m_CustomRenderQueue: -1 |
||||||
|
stringTagMap: {} |
||||||
|
disabledShaderPasses: [] |
||||||
|
m_SavedProperties: |
||||||
|
serializedVersion: 3 |
||||||
|
m_TexEnvs: |
||||||
|
- _BumpMap: |
||||||
|
m_Texture: {fileID: 2800000, guid: ed1b2724730cc594aa8c79b70d2deb24, type: 3} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _DetailAlbedoMap: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _DetailMask: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _DetailNormalMap: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _EmissionMap: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _MainTex: |
||||||
|
m_Texture: {fileID: 2800000, guid: f1ba7622665bf7e44aee3f43fc62e51d, type: 3} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _MetallicGlossMap: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _OcclusionMap: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
- _ParallaxMap: |
||||||
|
m_Texture: {fileID: 0} |
||||||
|
m_Scale: {x: 1, y: 1} |
||||||
|
m_Offset: {x: 0, y: 0} |
||||||
|
m_Ints: [] |
||||||
|
m_Floats: |
||||||
|
- _BumpScale: 1 |
||||||
|
- _Cutoff: 0.5 |
||||||
|
- _DetailNormalMapScale: 1 |
||||||
|
- _DstBlend: 0 |
||||||
|
- _GlossMapScale: 1 |
||||||
|
- _Glossiness: 0.5 |
||||||
|
- _GlossyReflections: 1 |
||||||
|
- _Metallic: 0 |
||||||
|
- _Mode: 0 |
||||||
|
- _OcclusionStrength: 1 |
||||||
|
- _Parallax: 0.02 |
||||||
|
- _SmoothnessTextureChannel: 0 |
||||||
|
- _SpecularHighlights: 1 |
||||||
|
- _SrcBlend: 1 |
||||||
|
- _UVSec: 0 |
||||||
|
- _ZWrite: 1 |
||||||
|
m_Colors: |
||||||
|
- _Color: {r: 1, g: 1, b: 1, a: 1} |
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1} |
||||||
|
m_BuildTextureStacks: [] |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: fee1dc970c1078347be135f66d2a333f |
||||||
|
NativeFormatImporter: |
||||||
|
externalObjects: {} |
||||||
|
mainObjectFileID: 2100000 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
Binary file not shown.
@ -0,0 +1,106 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 4026adb9c5f7450499245bc992637216 |
||||||
|
ModelImporter: |
||||||
|
serializedVersion: 21300 |
||||||
|
internalIDToNameTable: [] |
||||||
|
externalObjects: {} |
||||||
|
materials: |
||||||
|
materialImportMode: 2 |
||||||
|
materialName: 0 |
||||||
|
materialSearch: 1 |
||||||
|
materialLocation: 1 |
||||||
|
animations: |
||||||
|
legacyGenerateAnimations: 4 |
||||||
|
bakeSimulation: 0 |
||||||
|
resampleCurves: 1 |
||||||
|
optimizeGameObjects: 0 |
||||||
|
removeConstantScaleCurves: 1 |
||||||
|
motionNodeName: |
||||||
|
rigImportErrors: |
||||||
|
rigImportWarnings: |
||||||
|
animationImportErrors: |
||||||
|
animationImportWarnings: |
||||||
|
animationRetargetingWarnings: |
||||||
|
animationDoRetargetingWarnings: 0 |
||||||
|
importAnimatedCustomProperties: 0 |
||||||
|
importConstraints: 0 |
||||||
|
animationCompression: 1 |
||||||
|
animationRotationError: 0.5 |
||||||
|
animationPositionError: 0.5 |
||||||
|
animationScaleError: 0.5 |
||||||
|
animationWrapMode: 0 |
||||||
|
extraExposedTransformPaths: [] |
||||||
|
extraUserProperties: [] |
||||||
|
clipAnimations: [] |
||||||
|
isReadable: 0 |
||||||
|
meshes: |
||||||
|
lODScreenPercentages: [] |
||||||
|
globalScale: 1 |
||||||
|
meshCompression: 0 |
||||||
|
addColliders: 0 |
||||||
|
useSRGBMaterialColor: 1 |
||||||
|
sortHierarchyByName: 1 |
||||||
|
importVisibility: 1 |
||||||
|
importBlendShapes: 1 |
||||||
|
importCameras: 1 |
||||||
|
importLights: 1 |
||||||
|
nodeNameCollisionStrategy: 1 |
||||||
|
fileIdsGeneration: 2 |
||||||
|
swapUVChannels: 0 |
||||||
|
generateSecondaryUV: 0 |
||||||
|
useFileUnits: 1 |
||||||
|
keepQuads: 0 |
||||||
|
weldVertices: 1 |
||||||
|
bakeAxisConversion: 0 |
||||||
|
preserveHierarchy: 0 |
||||||
|
skinWeightsMode: 0 |
||||||
|
maxBonesPerVertex: 4 |
||||||
|
minBoneWeight: 0.001 |
||||||
|
optimizeBones: 1 |
||||||
|
meshOptimizationFlags: -1 |
||||||
|
indexFormat: 0 |
||||||
|
secondaryUVAngleDistortion: 8 |
||||||
|
secondaryUVAreaDistortion: 15.000001 |
||||||
|
secondaryUVHardAngle: 88 |
||||||
|
secondaryUVMarginMethod: 1 |
||||||
|
secondaryUVMinLightmapResolution: 40 |
||||||
|
secondaryUVMinObjectScale: 1 |
||||||
|
secondaryUVPackMargin: 4 |
||||||
|
useFileScale: 1 |
||||||
|
tangentSpace: |
||||||
|
normalSmoothAngle: 60 |
||||||
|
normalImportMode: 0 |
||||||
|
tangentImportMode: 3 |
||||||
|
normalCalculationMode: 4 |
||||||
|
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 |
||||||
|
blendShapeNormalImportMode: 1 |
||||||
|
normalSmoothingSource: 0 |
||||||
|
referencedClips: [] |
||||||
|
importAnimation: 1 |
||||||
|
humanDescription: |
||||||
|
serializedVersion: 3 |
||||||
|
human: [] |
||||||
|
skeleton: [] |
||||||
|
armTwist: 0.5 |
||||||
|
foreArmTwist: 0.5 |
||||||
|
upperLegTwist: 0.5 |
||||||
|
legTwist: 0.5 |
||||||
|
armStretch: 0.05 |
||||||
|
legStretch: 0.05 |
||||||
|
feetSpacing: 0 |
||||||
|
globalScale: 1 |
||||||
|
rootMotionBoneName: |
||||||
|
hasTranslationDoF: 0 |
||||||
|
hasExtraRoot: 0 |
||||||
|
skeletonHasParents: 1 |
||||||
|
lastHumanDescriptionAvatarSource: {instanceID: 0} |
||||||
|
autoGenerateAvatarMappingIfUnspecified: 1 |
||||||
|
animationType: 2 |
||||||
|
humanoidOversampling: 1 |
||||||
|
avatarSetup: 0 |
||||||
|
addHumanoidExtraRootOnlyWhenUsingAvatar: 1 |
||||||
|
remapMaterialsIfMaterialImportModeIsNone: 0 |
||||||
|
additionalBone: 0 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
|
After Width: | Height: | Size: 2.2 MiB |
@ -0,0 +1,135 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: b7117ed1afac5d34da92761a8d5697c7 |
||||||
|
TextureImporter: |
||||||
|
internalIDToNameTable: [] |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 12 |
||||||
|
mipmaps: |
||||||
|
mipMapMode: 0 |
||||||
|
enableMipMap: 1 |
||||||
|
sRGBTexture: 0 |
||||||
|
linearTexture: 0 |
||||||
|
fadeOut: 0 |
||||||
|
borderMipMap: 0 |
||||||
|
mipMapsPreserveCoverage: 0 |
||||||
|
alphaTestReferenceValue: 0.5 |
||||||
|
mipMapFadeDistanceStart: 1 |
||||||
|
mipMapFadeDistanceEnd: 3 |
||||||
|
bumpmap: |
||||||
|
convertToNormalMap: 0 |
||||||
|
externalNormalMap: 0 |
||||||
|
heightScale: 0.25 |
||||||
|
normalMapFilter: 0 |
||||||
|
isReadable: 0 |
||||||
|
streamingMipmaps: 0 |
||||||
|
streamingMipmapsPriority: 0 |
||||||
|
vTOnly: 0 |
||||||
|
ignoreMasterTextureLimit: 0 |
||||||
|
grayScaleToAlpha: 0 |
||||||
|
generateCubemap: 6 |
||||||
|
cubemapConvolution: 0 |
||||||
|
seamlessCubemap: 0 |
||||||
|
textureFormat: 1 |
||||||
|
maxTextureSize: 2048 |
||||||
|
textureSettings: |
||||||
|
serializedVersion: 2 |
||||||
|
filterMode: 1 |
||||||
|
aniso: 1 |
||||||
|
mipBias: 0 |
||||||
|
wrapU: 0 |
||||||
|
wrapV: 0 |
||||||
|
wrapW: 0 |
||||||
|
nPOTScale: 1 |
||||||
|
lightmap: 0 |
||||||
|
compressionQuality: 50 |
||||||
|
spriteMode: 0 |
||||||
|
spriteExtrude: 1 |
||||||
|
spriteMeshType: 1 |
||||||
|
alignment: 0 |
||||||
|
spritePivot: {x: 0.5, y: 0.5} |
||||||
|
spritePixelsToUnits: 100 |
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
||||||
|
spriteGenerateFallbackPhysicsShape: 1 |
||||||
|
alphaUsage: 1 |
||||||
|
alphaIsTransparency: 0 |
||||||
|
spriteTessellationDetail: -1 |
||||||
|
textureType: 1 |
||||||
|
textureShape: 1 |
||||||
|
singleChannelComponent: 0 |
||||||
|
flipbookRows: 1 |
||||||
|
flipbookColumns: 1 |
||||||
|
maxTextureSizeSet: 0 |
||||||
|
compressionQualitySet: 0 |
||||||
|
textureFormatSet: 0 |
||||||
|
ignorePngGamma: 0 |
||||||
|
applyGammaDecoding: 0 |
||||||
|
cookieLightType: 0 |
||||||
|
platformSettings: |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: DefaultTexturePlatform |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: Standalone |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: Server |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
- serializedVersion: 3 |
||||||
|
buildTarget: Android |
||||||
|
maxTextureSize: 2048 |
||||||
|
resizeAlgorithm: 0 |
||||||
|
textureFormat: -1 |
||||||
|
textureCompression: 1 |
||||||
|
compressionQuality: 50 |
||||||
|
crunchedCompression: 0 |
||||||
|
allowsAlphaSplitting: 0 |
||||||
|
overridden: 0 |
||||||
|
androidETC2FallbackOverride: 0 |
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0 |
||||||
|
spriteSheet: |
||||||
|
serializedVersion: 2 |
||||||
|
sprites: [] |
||||||
|
outline: [] |
||||||
|
physicsShape: [] |
||||||
|
bones: [] |
||||||
|
spriteID: |
||||||
|
internalID: 0 |
||||||
|
vertices: [] |
||||||
|
indices: |
||||||
|
edges: [] |
||||||
|
weights: [] |
||||||
|
secondaryTextures: [] |
||||||
|
nameFileIdTable: {} |
||||||
|
spritePackingTag: |
||||||
|
pSDRemoveMatte: 0 |
||||||
|
pSDShowRemoveMatteOption: 0 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
Binary file not shown.
@ -0,0 +1,106 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: a380529329fc6ff458d94d5795efaa28 |
||||||
|
ModelImporter: |
||||||
|
serializedVersion: 21300 |
||||||
|
internalIDToNameTable: [] |
||||||
|
externalObjects: {} |
||||||
|
materials: |
||||||
|
materialImportMode: 2 |
||||||
|
materialName: 0 |
||||||
|
materialSearch: 1 |
||||||
|
materialLocation: 1 |
||||||
|
animations: |
||||||
|
legacyGenerateAnimations: 4 |
||||||
|
bakeSimulation: 0 |
||||||
|
resampleCurves: 1 |
||||||
|
optimizeGameObjects: 0 |
||||||
|
removeConstantScaleCurves: 1 |
||||||
|
motionNodeName: |
||||||
|
rigImportErrors: |
||||||
|
rigImportWarnings: |
||||||
|
animationImportErrors: |
||||||
|
animationImportWarnings: |
||||||
|
animationRetargetingWarnings: |
||||||
|
animationDoRetargetingWarnings: 0 |
||||||
|
importAnimatedCustomProperties: 0 |
||||||
|
importConstraints: 0 |
||||||
|
animationCompression: 1 |
||||||
|
animationRotationError: 0.5 |
||||||
|
animationPositionError: 0.5 |
||||||
|
animationScaleError: 0.5 |
||||||
|
animationWrapMode: 0 |
||||||
|
extraExposedTransformPaths: [] |
||||||
|
extraUserProperties: [] |
||||||
|
clipAnimations: [] |
||||||
|
isReadable: 0 |
||||||
|
meshes: |
||||||
|
lODScreenPercentages: [] |
||||||
|
globalScale: 1 |
||||||
|
meshCompression: 0 |
||||||
|
addColliders: 0 |
||||||
|
useSRGBMaterialColor: 1 |
||||||
|
sortHierarchyByName: 1 |
||||||
|
importVisibility: 1 |
||||||
|
importBlendShapes: 1 |
||||||
|
importCameras: 1 |
||||||
|
importLights: 1 |
||||||
|
nodeNameCollisionStrategy: 1 |
||||||
|
fileIdsGeneration: 2 |
||||||
|
swapUVChannels: 0 |
||||||
|
generateSecondaryUV: 0 |
||||||
|
useFileUnits: 1 |
||||||
|
keepQuads: 0 |
||||||
|
weldVertices: 1 |
||||||
|
bakeAxisConversion: 0 |
||||||
|
preserveHierarchy: 0 |
||||||
|
skinWeightsMode: 0 |
||||||
|
maxBonesPerVertex: 4 |
||||||
|
minBoneWeight: 0.001 |
||||||
|
optimizeBones: 1 |
||||||
|
meshOptimizationFlags: -1 |
||||||
|
indexFormat: 0 |
||||||
|
secondaryUVAngleDistortion: 8 |
||||||
|
secondaryUVAreaDistortion: 15.000001 |
||||||
|
secondaryUVHardAngle: 88 |
||||||
|
secondaryUVMarginMethod: 1 |
||||||
|
secondaryUVMinLightmapResolution: 40 |
||||||
|
secondaryUVMinObjectScale: 1 |
||||||
|
secondaryUVPackMargin: 4 |
||||||
|
useFileScale: 1 |
||||||
|
tangentSpace: |
||||||
|
normalSmoothAngle: 60 |
||||||
|
normalImportMode: 0 |
||||||
|
tangentImportMode: 3 |
||||||
|
normalCalculationMode: 4 |
||||||
|
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 |
||||||
|
blendShapeNormalImportMode: 1 |
||||||
|
normalSmoothingSource: 0 |
||||||
|
referencedClips: [] |
||||||
|
importAnimation: 1 |
||||||
|
humanDescription: |
||||||
|
serializedVersion: 3 |
||||||
|
human: [] |
||||||
|
skeleton: [] |
||||||
|
armTwist: 0.5 |
||||||
|
foreArmTwist: 0.5 |
||||||
|
upperLegTwist: 0.5 |
||||||
|
legTwist: 0.5 |
||||||
|
armStretch: 0.05 |
||||||
|
legStretch: 0.05 |
||||||
|
feetSpacing: 0 |
||||||
|
globalScale: 1 |
||||||
|
rootMotionBoneName: |
||||||
|
hasTranslationDoF: 0 |
||||||
|
hasExtraRoot: 0 |
||||||
|
skeletonHasParents: 1 |
||||||
|
lastHumanDescriptionAvatarSource: {instanceID: 0} |
||||||
|
autoGenerateAvatarMappingIfUnspecified: 1 |
||||||
|
animationType: 2 |
||||||
|
humanoidOversampling: 1 |
||||||
|
avatarSetup: 0 |
||||||
|
addHumanoidExtraRootOnlyWhenUsingAvatar: 1 |
||||||
|
remapMaterialsIfMaterialImportModeIsNone: 0 |
||||||
|
additionalBone: 0 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
Binary file not shown.
@ -0,0 +1,106 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: a97e694b4cf57344e896e7d52b5a3cb7 |
||||||
|
ModelImporter: |
||||||
|
serializedVersion: 21300 |
||||||
|
internalIDToNameTable: [] |
||||||
|
externalObjects: {} |
||||||
|
materials: |
||||||
|
materialImportMode: 2 |
||||||
|
materialName: 0 |
||||||
|
materialSearch: 1 |
||||||
|
materialLocation: 1 |
||||||
|
animations: |
||||||
|
legacyGenerateAnimations: 4 |
||||||
|
bakeSimulation: 0 |
||||||
|
resampleCurves: 1 |
||||||
|
optimizeGameObjects: 0 |
||||||
|
removeConstantScaleCurves: 1 |
||||||
|
motionNodeName: |
||||||
|
rigImportErrors: |
||||||
|
rigImportWarnings: |
||||||
|
animationImportErrors: |
||||||
|
animationImportWarnings: |
||||||
|
animationRetargetingWarnings: |
||||||
|
animationDoRetargetingWarnings: 0 |
||||||
|
importAnimatedCustomProperties: 0 |
||||||
|
importConstraints: 0 |
||||||
|
animationCompression: 1 |
||||||
|
animationRotationError: 0.5 |
||||||
|
animationPositionError: 0.5 |
||||||
|
animationScaleError: 0.5 |
||||||
|
animationWrapMode: 0 |
||||||
|
extraExposedTransformPaths: [] |
||||||
|
extraUserProperties: [] |
||||||
|
clipAnimations: [] |
||||||
|
isReadable: 0 |
||||||
|
meshes: |
||||||
|
lODScreenPercentages: [] |
||||||
|
globalScale: 1 |
||||||
|
meshCompression: 0 |
||||||
|
addColliders: 0 |
||||||
|
useSRGBMaterialColor: 1 |
||||||
|
sortHierarchyByName: 1 |
||||||
|
importVisibility: 1 |
||||||
|
importBlendShapes: 1 |
||||||
|
importCameras: 1 |
||||||
|
importLights: 1 |
||||||
|
nodeNameCollisionStrategy: 1 |
||||||
|
fileIdsGeneration: 2 |
||||||
|
swapUVChannels: 0 |
||||||
|
generateSecondaryUV: 0 |
||||||
|
useFileUnits: 1 |
||||||
|
keepQuads: 0 |
||||||
|
weldVertices: 1 |
||||||
|
bakeAxisConversion: 0 |
||||||
|
preserveHierarchy: 0 |
||||||
|
skinWeightsMode: 0 |
||||||
|
maxBonesPerVertex: 4 |
||||||
|
minBoneWeight: 0.001 |
||||||
|
optimizeBones: 1 |
||||||
|
meshOptimizationFlags: -1 |
||||||
|
indexFormat: 0 |
||||||
|
secondaryUVAngleDistortion: 8 |
||||||
|
secondaryUVAreaDistortion: 15.000001 |
||||||
|
secondaryUVHardAngle: 88 |
||||||
|
secondaryUVMarginMethod: 1 |
||||||
|
secondaryUVMinLightmapResolution: 40 |
||||||
|
secondaryUVMinObjectScale: 1 |
||||||
|
secondaryUVPackMargin: 4 |
||||||
|
useFileScale: 1 |
||||||
|
tangentSpace: |
||||||
|
normalSmoothAngle: 60 |
||||||
|
normalImportMode: 0 |
||||||
|
tangentImportMode: 3 |
||||||
|
normalCalculationMode: 4 |
||||||
|
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 |
||||||
|
blendShapeNormalImportMode: 1 |
||||||
|
normalSmoothingSource: 0 |
||||||
|
referencedClips: [] |
||||||
|
importAnimation: 1 |
||||||
|
humanDescription: |
||||||
|
serializedVersion: 3 |
||||||
|
human: [] |
||||||
|
skeleton: [] |
||||||
|
armTwist: 0.5 |
||||||
|
foreArmTwist: 0.5 |
||||||
|
upperLegTwist: 0.5 |
||||||
|
legTwist: 0.5 |
||||||
|
armStretch: 0.05 |
||||||
|
legStretch: 0.05 |
||||||
|
feetSpacing: 0 |
||||||
|
globalScale: 1 |
||||||
|
rootMotionBoneName: |
||||||
|
hasTranslationDoF: 0 |
||||||
|
hasExtraRoot: 0 |
||||||
|
skeletonHasParents: 1 |
||||||
|
lastHumanDescriptionAvatarSource: {instanceID: 0} |
||||||
|
autoGenerateAvatarMappingIfUnspecified: 1 |
||||||
|
animationType: 2 |
||||||
|
humanoidOversampling: 1 |
||||||
|
avatarSetup: 0 |
||||||
|
addHumanoidExtraRootOnlyWhenUsingAvatar: 1 |
||||||
|
remapMaterialsIfMaterialImportModeIsNone: 0 |
||||||
|
additionalBone: 0 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 47498d3022992524486e2ea3f3de9e03 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: e795db969e6a5a047869038d636dd7c6 |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: e30a5371746adb74d9f8d9f59d14fa2d |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 30a0fc1ea21ad564ebc2259f6e455e0d |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: d6c3a74f6e0c9304fab58096773efdd6 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,802 @@ |
|||||||
|
using NaughtyAttributes; |
||||||
|
using System.Collections.Generic; |
||||||
|
using TouchSocket.Core; |
||||||
|
using UnityEngine; |
||||||
|
//IMU UNITY统一位姿版 2025/12/15 4574验证 |
||||||
|
|
||||||
|
public class HandDriver : MonoBehaviour |
||||||
|
{ |
||||||
|
public Network Network; |
||||||
|
|
||||||
|
public enum HandType |
||||||
|
{ |
||||||
|
Left, |
||||||
|
Right |
||||||
|
} |
||||||
|
|
||||||
|
public enum Axis |
||||||
|
{ |
||||||
|
x, |
||||||
|
y, |
||||||
|
z, |
||||||
|
x_n, |
||||||
|
y_n, |
||||||
|
z_n |
||||||
|
} |
||||||
|
|
||||||
|
public string CharacterName = string.Empty; |
||||||
|
public HandType Hand; |
||||||
|
private Dictionary<string, Quaternion> _originQuaternionDic; |
||||||
|
|
||||||
|
public Transform Thumb1; |
||||||
|
public Transform Thumb2; |
||||||
|
public Transform Thumb3; |
||||||
|
|
||||||
|
public Transform Index1; |
||||||
|
public Transform Index2; |
||||||
|
public Transform Index3; |
||||||
|
|
||||||
|
public Transform Middle1; |
||||||
|
public Transform Middle2; |
||||||
|
public Transform Middle3; |
||||||
|
|
||||||
|
public Transform Ring1; |
||||||
|
public Transform Ring2; |
||||||
|
public Transform Ring3; |
||||||
|
|
||||||
|
public Transform Pinky1; |
||||||
|
public Transform Pinky2; |
||||||
|
public Transform Pinky3; |
||||||
|
|
||||||
|
public Transform Wrist; |
||||||
|
|
||||||
|
private bool _wristOffsetReady = false; |
||||||
|
private Quaternion _wristOffset; |
||||||
|
|
||||||
|
[Header("[Axis OffSet]")]
|
||||||
|
public Axis Pitch = Axis.x; |
||||||
|
|
||||||
|
public Axis Roll = Axis.y; |
||||||
|
|
||||||
|
public Axis Yaw = Axis.z; |
||||||
|
|
||||||
|
[Header("[IMU(On/Off)]")] public bool HasIMU = false;
|
||||||
|
|
||||||
|
[Header("[Thumb Root Coefficient]")][Range(0, 1)] public float coefficient = 0.6f;
|
||||||
|
|
||||||
|
[Header("[Thumb Root OffSet]")] public Vector3 Thumb1Offset;
|
||||||
|
|
||||||
|
[Header("[App Options]")]
|
||||||
|
public bool NeedRealTransfrom; |
||||||
|
|
||||||
|
//[HideInInspector] |
||||||
|
public bool UsingNetwork; |
||||||
|
|
||||||
|
public bool UsingAndroidService; |
||||||
|
|
||||||
|
[Header("[Vector3 Angles]")]
|
||||||
|
public Vector3 thumb1; |
||||||
|
public Vector3 thumb2; |
||||||
|
public Vector3 thumb3; |
||||||
|
|
||||||
|
public Vector3 index1; |
||||||
|
public Vector3 index2; |
||||||
|
public Vector3 index3; |
||||||
|
|
||||||
|
public Vector3 middle1; |
||||||
|
public Vector3 middle2; |
||||||
|
public Vector3 middle3; |
||||||
|
|
||||||
|
public Vector3 ring1; |
||||||
|
public Vector3 ring2; |
||||||
|
public Vector3 ring3; |
||||||
|
|
||||||
|
public Vector3 pinky1; |
||||||
|
public Vector3 pinky2; |
||||||
|
public Vector3 pinky3; |
||||||
|
|
||||||
|
[Header("[Controller Values]")]
|
||||||
|
public float Joy_X; |
||||||
|
public float Joy_Y; |
||||||
|
public bool Button_A; |
||||||
|
public bool Button_B; |
||||||
|
public bool Button_Joystick; |
||||||
|
public bool Button_Menu; |
||||||
|
public InputData inputData = new(); |
||||||
|
|
||||||
|
[Header("[Wrist Flat Target Euler]")]
|
||||||
|
public Vector3 FlatEuler = new Vector3(0f, 0f, -90f); |
||||||
|
|
||||||
|
|
||||||
|
[Header("[Vibration Control]")]
|
||||||
|
public string SendBackIP; |
||||||
|
public VibrationData vibrationData; |
||||||
|
|
||||||
|
[BoxGroup("Vibrator 1"), Min(0), Label("Million Second")] |
||||||
|
public int Duration1 = 20; |
||||||
|
[BoxGroup("Vibrator 1"), Range(4, 10)] |
||||||
|
public int Amplitude1 = 4; |
||||||
|
[Button] |
||||||
|
public void Vibrator_1Active() |
||||||
|
{ |
||||||
|
if (Network == null) return; |
||||||
|
SingleVirbator[] Virbators; |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(1, Duration1, Amplitude1), |
||||||
|
new SingleVirbator() |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(), |
||||||
|
new SingleVirbator(1, Duration1, Amplitude1), |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
Network.SendVibrationMsg(CharacterName, SendBackIP, vibrationData); |
||||||
|
} |
||||||
|
|
||||||
|
[BoxGroup("Vibrator 2"), Min(0), Label("Million Second")] |
||||||
|
public int Duration2 = 20; |
||||||
|
[BoxGroup("Vibrator 2"), Range(4, 10)] |
||||||
|
public int Amplitude2 = 4; |
||||||
|
[Button] |
||||||
|
public void Vibrator_2Active() |
||||||
|
{ |
||||||
|
if (Network == null) return; |
||||||
|
SingleVirbator[] Virbators; |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(2, Duration2, Amplitude2), |
||||||
|
new SingleVirbator() |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(), |
||||||
|
new SingleVirbator(2, Duration2, Amplitude2), |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
Network.SendVibrationMsg(CharacterName, SendBackIP, vibrationData); |
||||||
|
} |
||||||
|
[Button] |
||||||
|
public void BothActiveWithVibrator_1Parameters() |
||||||
|
{ |
||||||
|
if (Network == null) return; |
||||||
|
SingleVirbator[] Virbators; |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(3, Duration1, Amplitude1), |
||||||
|
new SingleVirbator() |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(), |
||||||
|
new SingleVirbator(3, Duration1, Amplitude1), |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
Network.SendVibrationMsg(CharacterName, SendBackIP, vibrationData); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Start is called before the first frame update |
||||||
|
void Start() |
||||||
|
{ |
||||||
|
if (GameObject.Find("Network") == null && (UsingAndroidService || UsingNetwork)) |
||||||
|
{ |
||||||
|
GameObject network = new GameObject("Network"); |
||||||
|
network.AddComponent<Network>(); |
||||||
|
Network = GameObject.Find("Network").GetComponent<Network>(); |
||||||
|
} |
||||||
|
else if (UsingAndroidService || UsingNetwork) |
||||||
|
{ |
||||||
|
Network = GameObject.Find("Network").GetComponent<Network>(); |
||||||
|
} |
||||||
|
|
||||||
|
_originQuaternionDic = new Dictionary<string, Quaternion>(); |
||||||
|
InitJoints(); |
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(SendBackIP) || UsingAndroidService) |
||||||
|
{ |
||||||
|
SendBackIP = "127.0.0.1"; |
||||||
|
} |
||||||
|
|
||||||
|
var Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(0, 0, 1), |
||||||
|
new SingleVirbator(0, 0, 1) |
||||||
|
}; |
||||||
|
|
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
|
||||||
|
if (UsingAndroidService) |
||||||
|
{ |
||||||
|
CharacterName = "AndroidService"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void InitJoints() |
||||||
|
{ |
||||||
|
_originQuaternionDic.AddOrUpdate(Thumb1.name, Thumb1.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Thumb2.name, Thumb2.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Thumb3.name, Thumb3.localRotation); |
||||||
|
|
||||||
|
_originQuaternionDic.AddOrUpdate(Index1.name, Index1.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Index2.name, Index2.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Index3.name, Index3.localRotation); |
||||||
|
|
||||||
|
_originQuaternionDic.AddOrUpdate(Middle1.name, Middle1.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Middle2.name, Middle2.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Middle3.name, Middle3.localRotation); |
||||||
|
|
||||||
|
_originQuaternionDic.AddOrUpdate(Ring1.name, Ring1.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Ring2.name, Ring2.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Ring3.name, Ring3.localRotation); |
||||||
|
|
||||||
|
_originQuaternionDic.AddOrUpdate(Pinky1.name, Pinky1.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Pinky2.name, Pinky2.localRotation); |
||||||
|
_originQuaternionDic.AddOrUpdate(Pinky3.name, Pinky3.localRotation); |
||||||
|
|
||||||
|
_originQuaternionDic.AddOrUpdate(Wrist.name, Wrist.localRotation); |
||||||
|
} |
||||||
|
|
||||||
|
private void Rotate(Transform tran, float angle, Axis angleType) |
||||||
|
{ |
||||||
|
if (!NeedRealTransfrom) return; |
||||||
|
|
||||||
|
float angleX = 0; |
||||||
|
float angleY = 0; |
||||||
|
float angleZ = 0; |
||||||
|
|
||||||
|
switch (angleType) |
||||||
|
{ |
||||||
|
case Axis.x_n: |
||||||
|
angleX = -angle; |
||||||
|
break; |
||||||
|
case Axis.y_n: |
||||||
|
angleY = -angle; |
||||||
|
break; |
||||||
|
case Axis.z_n: |
||||||
|
angleZ = -angle; |
||||||
|
break; |
||||||
|
case Axis.x: |
||||||
|
angleX = angle; |
||||||
|
break; |
||||||
|
case Axis.y: |
||||||
|
angleY = angle; |
||||||
|
break; |
||||||
|
case Axis.z: |
||||||
|
angleZ = angle; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
tran.Rotate(angleX, angleY, angleZ); |
||||||
|
} |
||||||
|
|
||||||
|
private Vector3 ConvertAngleToVec3(Vector3 current, float angle, Axis angleType) |
||||||
|
{ |
||||||
|
float angleX = 0; |
||||||
|
float angleY = 0; |
||||||
|
float angleZ = 0; |
||||||
|
|
||||||
|
switch (angleType) |
||||||
|
{ |
||||||
|
case Axis.x_n: |
||||||
|
angleX = -angle; |
||||||
|
break; |
||||||
|
case Axis.y_n: |
||||||
|
angleY = -angle; |
||||||
|
break; |
||||||
|
case Axis.z_n: |
||||||
|
angleZ = -angle; |
||||||
|
break; |
||||||
|
case Axis.x: |
||||||
|
angleX = angle; |
||||||
|
break; |
||||||
|
case Axis.y: |
||||||
|
angleY = angle; |
||||||
|
break; |
||||||
|
case Axis.z: |
||||||
|
angleZ = angle; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return current + new Vector3(angleX, angleY, angleZ); |
||||||
|
} |
||||||
|
|
||||||
|
private void ResetRotation(Transform trans) |
||||||
|
{ |
||||||
|
if (_originQuaternionDic.TryGetValue(trans.name, out Quaternion rot)) |
||||||
|
{ |
||||||
|
trans.localRotation = rot; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void GetVec3Value(Vector3[] value) |
||||||
|
{ |
||||||
|
thumb1 = value[0]; |
||||||
|
thumb2 = value[1]; |
||||||
|
thumb3 = value[2]; |
||||||
|
|
||||||
|
index1 = value[3]; |
||||||
|
index2 = value[4]; |
||||||
|
index3 = value[5]; |
||||||
|
|
||||||
|
middle1 = value[6]; |
||||||
|
middle2 = value[7]; |
||||||
|
middle3 = value[8]; |
||||||
|
|
||||||
|
ring1 = value[9]; |
||||||
|
ring2 = value[10]; |
||||||
|
ring3 = value[11]; |
||||||
|
|
||||||
|
pinky1 = value[12]; |
||||||
|
pinky2 = value[13]; |
||||||
|
pinky3 = value[14]; |
||||||
|
} |
||||||
|
|
||||||
|
// Update is called once per frame |
||||||
|
void Update() |
||||||
|
{ |
||||||
|
|
||||||
|
if (UsingNetwork || UsingAndroidService) |
||||||
|
{ |
||||||
|
UpdateThumb(); |
||||||
|
UpdateIndex(); |
||||||
|
UpdateMiddle(); |
||||||
|
UpdateRing(); |
||||||
|
UpdatePinky(); |
||||||
|
|
||||||
|
UpdateWrist(); |
||||||
|
UpdateController(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateWrist() |
||||||
|
{ |
||||||
|
if (!HasIMU || Network == null || Wrist == null) |
||||||
|
return; |
||||||
|
|
||||||
|
bool isRight = Hand != HandType.Left; |
||||||
|
|
||||||
|
Quaternion imuRaw = GetImuRawQuaternion(); |
||||||
|
Quaternion imuUnity = ConvertQuaternion(imuRaw, isRight); |
||||||
|
|
||||||
|
if (_wristOffsetReady) |
||||||
|
{ |
||||||
|
Wrist.localRotation = _wristOffset * imuUnity; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Wrist.localRotation = imuUnity; |
||||||
|
} |
||||||
|
/* |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
Quaternion quat_r = new Quaternion( |
||||||
|
Network.Convert2Angle(CharacterName, "l26"),//x |
||||||
|
Network.Convert2Angle(CharacterName, "l25"),//y |
||||||
|
Network.Convert2Angle(CharacterName, "l27"),//z |
||||||
|
Network.Convert2Angle(CharacterName, "l24"));//w |
||||||
|
ResetRotation(Wrist); |
||||||
|
quat_r = ConvertQuaternion(quat_r); |
||||||
|
Wrist.rotation = quat_r; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Quaternion quat_r = new Quaternion(Network.Convert2Angle(CharacterName, "r26"), |
||||||
|
Network.Convert2Angle(CharacterName, "r25"), |
||||||
|
Network.Convert2Angle(CharacterName, "r27"), Network.Convert2Angle(CharacterName, "r24")); |
||||||
|
ResetRotation(Wrist); |
||||||
|
quat_r = ConvertQuaternion(quat_r); |
||||||
|
Wrist.rotation = quat_r; |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateController() |
||||||
|
{ |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
Joy_X = Network.Convert2Angle(CharacterName, "l_joyX"); |
||||||
|
Joy_Y = Network.Convert2Angle(CharacterName, "l_joyY"); |
||||||
|
Button_A = Network.Convert2Bool(CharacterName, "l_aButton"); |
||||||
|
Button_B = Network.Convert2Bool(CharacterName, "l_bButton"); |
||||||
|
Button_Joystick = Network.Convert2Bool(CharacterName, "l_joyButton"); |
||||||
|
Button_Menu = Network.Convert2Bool(CharacterName, "l_menu"); |
||||||
|
inputData.joyX = Joy_X; |
||||||
|
inputData.joyY = Joy_Y; |
||||||
|
inputData.aButton = Button_A; |
||||||
|
inputData.bButton = Button_B; |
||||||
|
inputData.joyButton = Button_Joystick; |
||||||
|
inputData.menu = Button_Menu; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Joy_X = Network.Convert2Angle(CharacterName, "r_joyX"); |
||||||
|
Joy_Y = Network.Convert2Angle(CharacterName, "r_joyY"); |
||||||
|
Button_A = Network.Convert2Bool(CharacterName, "r_aButton"); |
||||||
|
Button_B = Network.Convert2Bool(CharacterName, "r_bButton"); |
||||||
|
Button_Joystick = Network.Convert2Bool(CharacterName, "r_joyButton"); |
||||||
|
Button_Menu = Network.Convert2Bool(CharacterName, "r_menu"); |
||||||
|
inputData.joyX = Joy_X; |
||||||
|
inputData.joyY = Joy_Y; |
||||||
|
inputData.aButton = Button_A; |
||||||
|
inputData.bButton = Button_B; |
||||||
|
inputData.joyButton = Button_Joystick; |
||||||
|
inputData.menu = Button_Menu; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Quaternion ConvertQuaternion(Quaternion quat, bool isRightHand) |
||||||
|
{ |
||||||
|
|
||||||
|
|
||||||
|
Quaternion q = new Quaternion(quat.x, quat.y, quat.z, quat.w); |
||||||
|
|
||||||
|
Vector3 e = q.eulerAngles; |
||||||
|
// Euler(z, -x, y) |
||||||
|
Quaternion result = Quaternion.Euler(e.z, -e.x, e.y); |
||||||
|
if (isRightHand) |
||||||
|
{ |
||||||
|
result = Quaternion.Euler(-e.z , -e.x , -e.y ); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
/* |
||||||
|
Quaternion quat_r = new Quaternion(quat.x, quat.y, quat.z, quat.w); |
||||||
|
quat_r = Quaternion.Euler(quat_r.eulerAngles.z, -quat_r.eulerAngles.x, quat_r.eulerAngles.y); |
||||||
|
return quat_r; |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateThumb() |
||||||
|
{ |
||||||
|
if (NeedRealTransfrom) |
||||||
|
{ |
||||||
|
ResetRotation(Thumb1); |
||||||
|
ResetRotation(Thumb2); |
||||||
|
ResetRotation(Thumb3); |
||||||
|
} |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
var thumb3Pitch = Network.Convert2Angle(CharacterName, "l0"); |
||||||
|
var thumb2Pitch = Network.Convert2Angle(CharacterName, "l1"); |
||||||
|
var thumb1Pitch = Network.Convert2Angle(CharacterName, "l2") * coefficient + Thumb1Offset.y; |
||||||
|
var thumb1Yaw = Network.Convert2Angle(CharacterName, "l3") + Thumb1Offset.z; |
||||||
|
var thumb1Roll = Network.Convert2Angle(CharacterName, "l20") + Thumb1Offset.x; |
||||||
|
|
||||||
|
thumb3 = ConvertAngleToVec3(Vector3.zero, thumb3Pitch, Pitch); |
||||||
|
thumb2 = ConvertAngleToVec3(Vector3.zero, thumb2Pitch, Pitch); |
||||||
|
thumb1 = ConvertAngleToVec3(Vector3.zero, thumb1Pitch, Pitch); |
||||||
|
thumb1 = ConvertAngleToVec3(thumb1, thumb1Yaw, Yaw); |
||||||
|
thumb1 = ConvertAngleToVec3(thumb1, thumb1Roll, Roll); |
||||||
|
|
||||||
|
Rotate(Thumb3, thumb3Pitch, Pitch); |
||||||
|
Rotate(Thumb2, thumb2Pitch, Pitch); |
||||||
|
Rotate(Thumb1, thumb1Pitch, Pitch); |
||||||
|
Rotate(Thumb1, thumb1Yaw, Yaw); |
||||||
|
Rotate(Thumb1, thumb1Roll, Roll); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
|
||||||
|
var thumb3Pitch = Network.Convert2Angle(CharacterName, "r0"); |
||||||
|
var thumb2Pitch = Network.Convert2Angle(CharacterName, "r1"); |
||||||
|
var thumb1Pitch = Network.Convert2Angle(CharacterName, "r2") * coefficient + Thumb1Offset.y; |
||||||
|
var thumb1Yaw = Network.Convert2Angle(CharacterName, "r3") + Thumb1Offset.z; |
||||||
|
var thumb1Roll = Network.Convert2Angle(CharacterName, "r20") + Thumb1Offset.x; |
||||||
|
|
||||||
|
thumb3 = ConvertAngleToVec3(Vector3.zero, thumb3Pitch, Pitch); |
||||||
|
thumb2 = ConvertAngleToVec3(Vector3.zero, thumb2Pitch, Pitch); |
||||||
|
thumb1 = ConvertAngleToVec3(Vector3.zero, thumb1Pitch, Pitch); |
||||||
|
thumb1 = ConvertAngleToVec3(thumb1, thumb1Yaw, Yaw); |
||||||
|
thumb1 = ConvertAngleToVec3(thumb1, thumb1Roll, Roll); |
||||||
|
|
||||||
|
Rotate(Thumb3, thumb3Pitch, Pitch); |
||||||
|
Rotate(Thumb2, thumb2Pitch, Pitch); |
||||||
|
Rotate(Thumb1, thumb1Pitch, Pitch); |
||||||
|
Rotate(Thumb1, thumb1Yaw, Yaw); |
||||||
|
Rotate(Thumb1, thumb1Roll, Roll); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateIndex() |
||||||
|
{ |
||||||
|
if (NeedRealTransfrom) |
||||||
|
{ |
||||||
|
ResetRotation(Index1); |
||||||
|
ResetRotation(Index2); |
||||||
|
ResetRotation(Index3); |
||||||
|
} |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
var index3Pitch = Network.Convert2Angle(CharacterName, "l4"); |
||||||
|
var index2Pitch = Network.Convert2Angle(CharacterName, "l5"); |
||||||
|
var index1Pitch = Network.Convert2Angle(CharacterName, "l6"); |
||||||
|
var index1Yaw = Network.Convert2Angle(CharacterName, "l7"); |
||||||
|
var index1Roll = Network.Convert2Angle(CharacterName, "l21"); |
||||||
|
|
||||||
|
index3 = ConvertAngleToVec3(Vector3.zero, index3Pitch, Pitch); |
||||||
|
index2 = ConvertAngleToVec3(Vector3.zero, index2Pitch, Pitch); |
||||||
|
index1 = ConvertAngleToVec3(Vector3.zero, index1Pitch, Pitch); |
||||||
|
index1 = ConvertAngleToVec3(index1, index1Yaw, Yaw); |
||||||
|
index1 = ConvertAngleToVec3(index1, index1Roll, Roll); |
||||||
|
|
||||||
|
Rotate(Index3, index3Pitch, Pitch); |
||||||
|
Rotate(Index2, index2Pitch, Pitch); |
||||||
|
Rotate(Index1, index1Pitch, Pitch); |
||||||
|
Rotate(Index1, index1Yaw, Yaw); |
||||||
|
Rotate(Index1, index1Roll, Roll); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
var index3Pitch = Network.Convert2Angle(CharacterName, "r4"); |
||||||
|
var index2Pitch = Network.Convert2Angle(CharacterName, "r5"); |
||||||
|
var index1Pitch = Network.Convert2Angle(CharacterName, "r6"); |
||||||
|
var index1Yaw = Network.Convert2Angle(CharacterName, "r7"); |
||||||
|
var index1Roll = Network.Convert2Angle(CharacterName, "r21"); |
||||||
|
|
||||||
|
index3 = ConvertAngleToVec3(Vector3.zero, index3Pitch, Pitch); |
||||||
|
index2 = ConvertAngleToVec3(Vector3.zero, index2Pitch, Pitch); |
||||||
|
index1 = ConvertAngleToVec3(Vector3.zero, index1Pitch, Pitch); |
||||||
|
index1 = ConvertAngleToVec3(index1, index1Yaw, Yaw); |
||||||
|
index1 = ConvertAngleToVec3(index1, index1Roll, Roll); |
||||||
|
|
||||||
|
Rotate(Index3, index3Pitch, Pitch); |
||||||
|
Rotate(Index2, index2Pitch, Pitch); |
||||||
|
Rotate(Index1, index1Pitch, Pitch); |
||||||
|
Rotate(Index1, index1Yaw, Yaw); |
||||||
|
Rotate(Index1, index1Roll, Roll); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateMiddle() |
||||||
|
{ |
||||||
|
if (NeedRealTransfrom) |
||||||
|
{ |
||||||
|
ResetRotation(Middle1); |
||||||
|
ResetRotation(Middle2); |
||||||
|
ResetRotation(Middle3); |
||||||
|
} |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
var middle3Pitch = Network.Convert2Angle(CharacterName, "l8"); |
||||||
|
var middle2Pitch = Network.Convert2Angle(CharacterName, "l9"); |
||||||
|
var middle1Pitch = Network.Convert2Angle(CharacterName, "l10"); |
||||||
|
var middle1Yaw = Network.Convert2Angle(CharacterName, "l11"); |
||||||
|
|
||||||
|
middle3 = ConvertAngleToVec3(Vector3.zero, middle3Pitch, Pitch); |
||||||
|
middle2 = ConvertAngleToVec3(Vector3.zero, middle2Pitch, Pitch); |
||||||
|
middle1 = ConvertAngleToVec3(Vector3.zero, middle1Pitch, Pitch); |
||||||
|
middle1 = ConvertAngleToVec3(middle1, middle1Yaw, Yaw); |
||||||
|
|
||||||
|
Rotate(Middle3, middle3Pitch, Pitch); |
||||||
|
Rotate(Middle2, middle2Pitch, Pitch); |
||||||
|
Rotate(Middle1, middle1Pitch, Pitch); |
||||||
|
Rotate(Middle1, middle1Yaw, Yaw); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
var middle3Pitch = Network.Convert2Angle(CharacterName, "r8"); |
||||||
|
var middle2Pitch = Network.Convert2Angle(CharacterName, "r9"); |
||||||
|
var middle1Pitch = Network.Convert2Angle(CharacterName, "r10"); |
||||||
|
var middle1Yaw = Network.Convert2Angle(CharacterName, "r11"); |
||||||
|
|
||||||
|
middle3 = ConvertAngleToVec3(Vector3.zero, middle3Pitch, Pitch); |
||||||
|
middle2 = ConvertAngleToVec3(Vector3.zero, middle2Pitch, Pitch); |
||||||
|
middle1 = ConvertAngleToVec3(Vector3.zero, middle1Pitch, Pitch); |
||||||
|
middle1 = ConvertAngleToVec3(middle1, middle1Yaw, Yaw); |
||||||
|
|
||||||
|
Rotate(Middle3, middle3Pitch, Pitch); |
||||||
|
Rotate(Middle2, middle2Pitch, Pitch); |
||||||
|
Rotate(Middle1, middle1Pitch, Pitch); |
||||||
|
Rotate(Middle1, middle1Yaw, Yaw); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdateRing() |
||||||
|
{ |
||||||
|
if (NeedRealTransfrom) |
||||||
|
{ |
||||||
|
ResetRotation(Ring1); |
||||||
|
ResetRotation(Ring2); |
||||||
|
ResetRotation(Ring3); |
||||||
|
} |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
var ring3Pitch = Network.Convert2Angle(CharacterName, "l12"); |
||||||
|
var ring2Pitch = Network.Convert2Angle(CharacterName, "l13"); |
||||||
|
var ring1Pitch = Network.Convert2Angle(CharacterName, "l14"); |
||||||
|
var ring1Yaw = Network.Convert2Angle(CharacterName, "l15"); |
||||||
|
|
||||||
|
ring3 = ConvertAngleToVec3(Vector3.zero, ring3Pitch, Pitch); |
||||||
|
ring2 = ConvertAngleToVec3(Vector3.zero, ring2Pitch, Pitch); |
||||||
|
ring1 = ConvertAngleToVec3(Vector3.zero, ring1Pitch, Pitch); |
||||||
|
ring1 = ConvertAngleToVec3(ring1, ring1Yaw, Yaw); |
||||||
|
|
||||||
|
Rotate(Ring3, ring3Pitch, Pitch); |
||||||
|
Rotate(Ring2, ring2Pitch, Pitch); |
||||||
|
Rotate(Ring1, ring1Pitch, Pitch); |
||||||
|
Rotate(Ring1, ring1Yaw, Yaw); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
var ring3Pitch = Network.Convert2Angle(CharacterName, "r12"); |
||||||
|
var ring2Pitch = Network.Convert2Angle(CharacterName, "r13"); |
||||||
|
var ring1Pitch = Network.Convert2Angle(CharacterName, "r14"); |
||||||
|
var ring1Yaw = Network.Convert2Angle(CharacterName, "r15"); |
||||||
|
|
||||||
|
ring3 = ConvertAngleToVec3(Vector3.zero, ring3Pitch, Pitch); |
||||||
|
ring2 = ConvertAngleToVec3(Vector3.zero, ring2Pitch, Pitch); |
||||||
|
ring1 = ConvertAngleToVec3(Vector3.zero, ring1Pitch, Pitch); |
||||||
|
ring1 = ConvertAngleToVec3(ring1, ring1Yaw, Yaw); |
||||||
|
|
||||||
|
Rotate(Ring3, ring3Pitch, Pitch); |
||||||
|
Rotate(Ring2, ring2Pitch, Pitch); |
||||||
|
Rotate(Ring1, ring1Pitch, Pitch); |
||||||
|
Rotate(Ring1, ring1Yaw, Yaw); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void UpdatePinky() |
||||||
|
{ |
||||||
|
if (NeedRealTransfrom) |
||||||
|
{ |
||||||
|
ResetRotation(Pinky1); |
||||||
|
ResetRotation(Pinky2); |
||||||
|
ResetRotation(Pinky3); |
||||||
|
} |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
var pinky3Pitch = Network.Convert2Angle(CharacterName, "l16"); |
||||||
|
var pinky2Pitch = Network.Convert2Angle(CharacterName, "l17"); |
||||||
|
var pinky1Pitch = Network.Convert2Angle(CharacterName, "l18"); |
||||||
|
var pinky1Yaw = Network.Convert2Angle(CharacterName, "l19"); |
||||||
|
var pinky1Roll = Network.Convert2Angle(CharacterName, "l22"); |
||||||
|
|
||||||
|
pinky3 = ConvertAngleToVec3(Vector3.zero, pinky3Pitch, Pitch); |
||||||
|
pinky2 = ConvertAngleToVec3(Vector3.zero, pinky2Pitch, Pitch); |
||||||
|
pinky1 = ConvertAngleToVec3(Vector3.zero, pinky1Pitch, Pitch); |
||||||
|
pinky1 = ConvertAngleToVec3(pinky1, pinky1Yaw, Yaw); |
||||||
|
pinky1 = ConvertAngleToVec3(pinky1, pinky1Roll, Roll); |
||||||
|
|
||||||
|
Rotate(Pinky3, pinky3Pitch, Pitch); |
||||||
|
Rotate(Pinky2, pinky2Pitch, Pitch); |
||||||
|
Rotate(Pinky1, pinky1Pitch, Pitch); |
||||||
|
Rotate(Pinky1, pinky1Yaw, Yaw); |
||||||
|
Rotate(Pinky1, pinky1Roll, Roll); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
var pinky3Pitch = Network.Convert2Angle(CharacterName, "r16"); |
||||||
|
var pinky2Pitch = Network.Convert2Angle(CharacterName, "r17"); |
||||||
|
var pinky1Pitch = Network.Convert2Angle(CharacterName, "r18"); |
||||||
|
var pinky1Yaw = Network.Convert2Angle(CharacterName, "r19"); |
||||||
|
var pinky1Roll = Network.Convert2Angle(CharacterName, "r22"); |
||||||
|
|
||||||
|
pinky3 = ConvertAngleToVec3(Vector3.zero, pinky3Pitch, Pitch); |
||||||
|
pinky2 = ConvertAngleToVec3(Vector3.zero, pinky2Pitch, Pitch); |
||||||
|
pinky1 = ConvertAngleToVec3(Vector3.zero, pinky1Pitch, Pitch); |
||||||
|
pinky1 = ConvertAngleToVec3(pinky1, pinky1Yaw, Yaw); |
||||||
|
pinky1 = ConvertAngleToVec3(pinky1, pinky1Roll, Roll); |
||||||
|
|
||||||
|
Rotate(Pinky3, pinky3Pitch, Pitch); |
||||||
|
Rotate(Pinky2, pinky2Pitch, Pitch); |
||||||
|
Rotate(Pinky1, pinky1Pitch, Pitch); |
||||||
|
Rotate(Pinky1, pinky1Yaw, Yaw); |
||||||
|
Rotate(Pinky1, pinky1Roll, Roll); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void ServiceVibrationControl(int VirbatorIndex = 1, int DurationSecond = 20, int Strength = 10) |
||||||
|
{ |
||||||
|
if (Network == null || !UsingAndroidService) return; |
||||||
|
|
||||||
|
SingleVirbator[] Virbators; |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(VirbatorIndex, DurationSecond, Strength), |
||||||
|
new SingleVirbator() |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
Virbators = new SingleVirbator[2] |
||||||
|
{ |
||||||
|
new SingleVirbator(), |
||||||
|
new SingleVirbator(VirbatorIndex, DurationSecond, Strength), |
||||||
|
}; |
||||||
|
vibrationData = new VibrationData(Virbators); |
||||||
|
} |
||||||
|
Network.SendVibrationMsg("AndroidService", "127.0.0.1", vibrationData); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[NaughtyAttributes.Button("Calibrate This Hand (Flat)")] |
||||||
|
public void CalibrateWristFlat() |
||||||
|
{ |
||||||
|
if (!HasIMU || Network == null || Wrist == null) |
||||||
|
return; |
||||||
|
|
||||||
|
bool isRight = Hand != HandType.Left; |
||||||
|
|
||||||
|
//当前 IMU 姿态 -> Unity 坐标系 |
||||||
|
Quaternion imuRawFlat = GetImuRawQuaternion(); |
||||||
|
Quaternion imuUnityFlat = ConvertQuaternion(imuRawFlat, isRight); |
||||||
|
|
||||||
|
Quaternion baseFlat = Quaternion.Euler(FlatEuler); |
||||||
|
|
||||||
|
|
||||||
|
Quaternion modelFlat = isRight |
||||||
|
? Quaternion.Euler(0f, 0f, 0f) * baseFlat |
||||||
|
: baseFlat; |
||||||
|
|
||||||
|
//计算 offset |
||||||
|
_wristOffset = modelFlat * Quaternion.Inverse(imuUnityFlat); |
||||||
|
_wristOffsetReady = true; |
||||||
|
|
||||||
|
Wrist.localRotation = modelFlat; |
||||||
|
|
||||||
|
Debug.Log($"[{Hand}] Calibrated flat. imuFlat={imuUnityFlat.eulerAngles}, modelFlat={modelFlat.eulerAngles}"); |
||||||
|
} |
||||||
|
|
||||||
|
[NaughtyAttributes.Button("Calibrate ALL Hands (Flat)")] |
||||||
|
public void CalibrateAllHandsFlat() |
||||||
|
{ |
||||||
|
var drivers = FindObjectsOfType<HandDriver>(); |
||||||
|
|
||||||
|
foreach (var d in drivers) |
||||||
|
{ |
||||||
|
d.CalibrateWristFlat(); |
||||||
|
} |
||||||
|
|
||||||
|
Debug.Log($"[HandDriver] Calibrated {drivers.Length} hands."); |
||||||
|
} |
||||||
|
private Quaternion GetImuRawQuaternion() |
||||||
|
{ |
||||||
|
if (Hand == HandType.Left) |
||||||
|
{ |
||||||
|
return new Quaternion( |
||||||
|
Network.Convert2Angle(CharacterName, "l26"), // x |
||||||
|
Network.Convert2Angle(CharacterName, "l25"), // y |
||||||
|
Network.Convert2Angle(CharacterName, "l27"), // z |
||||||
|
Network.Convert2Angle(CharacterName, "l24") // w |
||||||
|
); |
||||||
|
} |
||||||
|
else // Right |
||||||
|
{ |
||||||
|
return new Quaternion( |
||||||
|
Network.Convert2Angle(CharacterName, "r26"), // x |
||||||
|
Network.Convert2Angle(CharacterName, "r25"), // y |
||||||
|
Network.Convert2Angle(CharacterName, "r27"), // z |
||||||
|
Network.Convert2Angle(CharacterName, "r24") // w |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: a892ab96c99c58e48953166e418f663b |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
//Struct InputData is a struct that contains all of the button, finger, and linear inputs. This is what's sent to the driver via the named pipe. |
||||||
|
public struct InputData |
||||||
|
{ |
||||||
|
|
||||||
|
public float joyX; //range: -1 -> 1 |
||||||
|
public float joyY; //range: -1 -> 1 |
||||||
|
public bool joyButton; |
||||||
|
public bool trgButton; |
||||||
|
public bool aButton; |
||||||
|
public bool bButton; |
||||||
|
public bool grab; |
||||||
|
//public bool pinch; |
||||||
|
public bool menu; |
||||||
|
//public bool calibrate; |
||||||
|
public bool trackpad_touch; |
||||||
|
public float trgValue; //range: 0 -> 1 |
||||||
|
|
||||||
|
//constructor that uses a 1d array for flexion. |
||||||
|
public InputData(float joyX, float joyY, bool joyButton, bool trgButton, |
||||||
|
bool aButton, bool bButton, bool grab, bool pinch, bool menu, bool calibrate, float trgValue,bool trackpad_touch) |
||||||
|
{ |
||||||
|
this.joyX = joyX; |
||||||
|
this.joyY = joyY; |
||||||
|
this.joyButton = joyButton; |
||||||
|
this.trgButton = trgButton; |
||||||
|
this.aButton = aButton; |
||||||
|
this.bButton = bButton; |
||||||
|
this.grab = grab; |
||||||
|
//this.pinch = pinch; |
||||||
|
this.menu = menu; |
||||||
|
//this.calibrate = calibrate; |
||||||
|
this.trgValue = trgValue; |
||||||
|
this.trackpad_touch = trackpad_touch; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: d4273e9f53dd73148970d6f4015d15bd |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,207 @@ |
|||||||
|
using Newtonsoft.Json.Linq; |
||||||
|
using System; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.IO; |
||||||
|
using System.Net; |
||||||
|
using System.Text; |
||||||
|
using TouchSocket.Core; |
||||||
|
using TouchSocket.Sockets; |
||||||
|
using UnityEngine; |
||||||
|
|
||||||
|
public class Network : MonoBehaviour |
||||||
|
{ |
||||||
|
|
||||||
|
public int Port = 5555; |
||||||
|
|
||||||
|
private UdpSession _udpClient; |
||||||
|
|
||||||
|
private ConcurrentDictionary<string, ConcurrentDictionary<string, string>> _deviceReadMessages; |
||||||
|
|
||||||
|
private StreamWriter writer; |
||||||
|
private string _txtPath; |
||||||
|
void Start() |
||||||
|
{ |
||||||
|
_txtPath = Application.dataPath + "stream.data"; |
||||||
|
_deviceReadMessages = new ConcurrentDictionary<string, ConcurrentDictionary<string, string>>(); |
||||||
|
_udpClient = new UdpSession(); |
||||||
|
|
||||||
|
_udpClient.Received += ReceiveMsg; |
||||||
|
|
||||||
|
_udpClient.Setup(new TouchSocketConfig() |
||||||
|
.SetBindIPHost(new IPHost(Port)) |
||||||
|
.SetUdpDataHandlingAdapter(() => new NormalUdpDataHandlingAdapter())) |
||||||
|
.Start(); |
||||||
|
|
||||||
|
Debug.Log("UDP Client Start!!"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void WriteIntoTxt(string message) |
||||||
|
{ |
||||||
|
FileInfo file = new FileInfo(_txtPath); |
||||||
|
if (!file.Exists) |
||||||
|
{ |
||||||
|
writer = file.CreateText(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
writer = file.AppendText(); |
||||||
|
} |
||||||
|
|
||||||
|
writer.WriteLine(message); |
||||||
|
writer.Flush(); |
||||||
|
writer.Dispose(); |
||||||
|
writer.Close(); |
||||||
|
} |
||||||
|
|
||||||
|
private void ReceiveMsg(EndPoint endpoint, ByteBlock byteblock, IRequestInfo requestinfo) |
||||||
|
{ |
||||||
|
string msg = Encoding.UTF8.GetString(byteblock.Buffer, 0, byteblock.Len); |
||||||
|
Debug.Log(msg); |
||||||
|
//WriteIntoTxt(msg); |
||||||
|
JObject obj = JObject.Parse(msg); |
||||||
|
var jps = obj.Properties(); |
||||||
|
foreach (var jp in jps) |
||||||
|
{ |
||||||
|
string role_name = jp.Name; |
||||||
|
|
||||||
|
JToken token = obj.GetValue(role_name); |
||||||
|
JArray array = token["Parameter"] as JArray; |
||||||
|
ConcurrentDictionary<string, string> _deviceMsg = new ConcurrentDictionary<string, string>(); |
||||||
|
for (int i = 0; i < array.Count; i++) |
||||||
|
{ |
||||||
|
JObject obj1 = array[i] as JObject; |
||||||
|
string key = obj1.GetValue("Name").ToString(); |
||||||
|
string value = obj1.GetValue("Value").ToString(); |
||||||
|
_deviceMsg.TryAdd(key, value); |
||||||
|
} |
||||||
|
if (_deviceReadMessages.ContainsKey(role_name)) |
||||||
|
{ |
||||||
|
_deviceReadMessages[role_name] = _deviceMsg; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
_deviceReadMessages.TryAdd(role_name, _deviceMsg); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
//JToken token = obj.GetValue("Device1_"+Port); |
||||||
|
//JArray array = token["Parameter"] as JArray; |
||||||
|
//for (int i = 0; i < array.Count; i++) |
||||||
|
//{ |
||||||
|
// JObject obj1 = array[i] as JObject; |
||||||
|
// string key = obj1.GetValue("Name").ToString(); |
||||||
|
// string value = obj1.GetValue("Value").ToString(); |
||||||
|
// _device1ReadMessages.AddOrUpdate(key, value); |
||||||
|
//} |
||||||
|
|
||||||
|
//JToken token2 = obj.GetValue("Device2_"+Port); |
||||||
|
//JArray array2 = token2["Parameter"] as JArray; |
||||||
|
//for (int i = 0; i < array.Count; i++) |
||||||
|
//{ |
||||||
|
// JObject obj1 = array2[i] as JObject; |
||||||
|
// string key = obj1.GetValue("Name").ToString(); |
||||||
|
// string value = obj1.GetValue("Value").ToString(); |
||||||
|
// _device2ReadMessages.AddOrUpdate(key, value); |
||||||
|
//} |
||||||
|
} |
||||||
|
|
||||||
|
// Update is called once per frame |
||||||
|
void Update() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public float Convert2Angle(string role_name, string key) |
||||||
|
{ |
||||||
|
float angle = 0; |
||||||
|
if (!string.IsNullOrEmpty(role_name)) |
||||||
|
{ |
||||||
|
if (!_deviceReadMessages.ContainsKey(role_name)) |
||||||
|
{ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
string str = _deviceReadMessages[role_name][key]; |
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(str)) |
||||||
|
{ |
||||||
|
angle = Single.Parse(str); |
||||||
|
} |
||||||
|
} |
||||||
|
return angle; |
||||||
|
} |
||||||
|
|
||||||
|
public bool Convert2Bool(string role_name, string key) |
||||||
|
{ |
||||||
|
bool flag = false; |
||||||
|
if (!string.IsNullOrEmpty(role_name)) |
||||||
|
{ |
||||||
|
if (!_deviceReadMessages.ContainsKey(role_name)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
string str = _deviceReadMessages[role_name][key]; |
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(str)) |
||||||
|
{ |
||||||
|
flag = bool.Parse(str); |
||||||
|
} |
||||||
|
} |
||||||
|
return flag; |
||||||
|
} |
||||||
|
|
||||||
|
public void SendVibrationMsg(string RoleName, string IP, VibrationData data) |
||||||
|
{ |
||||||
|
var json_role = new JObject(); |
||||||
|
var json_one = new JObject(); |
||||||
|
var parameterArrayLeft = new JArray(); |
||||||
|
var parameterArrayRight = new JArray(); |
||||||
|
|
||||||
|
var _Lpara_active = new JObject(); |
||||||
|
_Lpara_active.Add("Name", "Vibrators"); |
||||||
|
_Lpara_active.Add("Value", data.Virbators[0].ActiveCommand); |
||||||
|
|
||||||
|
var _Lpara_duration = new JObject(); |
||||||
|
_Lpara_duration.Add("Name", "Duration"); |
||||||
|
_Lpara_duration.Add("Value", data.Virbators[0].Duration); |
||||||
|
|
||||||
|
var _Lpara_amplitude = new JObject(); |
||||||
|
_Lpara_amplitude.Add("Name", "Amplitude"); |
||||||
|
_Lpara_amplitude.Add("Value", data.Virbators[0].Amplitude); |
||||||
|
|
||||||
|
parameterArrayLeft.Add(_Lpara_active); |
||||||
|
parameterArrayLeft.Add(_Lpara_duration); |
||||||
|
parameterArrayLeft.Add(_Lpara_amplitude); |
||||||
|
|
||||||
|
var _Rpara_active = new JObject(); |
||||||
|
_Rpara_active.Add("Name", "Vibrators"); |
||||||
|
_Rpara_active.Add("Value", data.Virbators[1].ActiveCommand); |
||||||
|
|
||||||
|
var _Rpara_duration = new JObject(); |
||||||
|
_Rpara_duration.Add("Name", "Duration"); |
||||||
|
_Rpara_duration.Add("Value", data.Virbators[1].Duration); |
||||||
|
|
||||||
|
var _Rpara_amplitude = new JObject(); |
||||||
|
_Rpara_amplitude.Add("Name", "Amplitude"); |
||||||
|
_Rpara_amplitude.Add("Value", data.Virbators[1].Amplitude); |
||||||
|
|
||||||
|
parameterArrayRight.Add(_Rpara_active); |
||||||
|
parameterArrayRight.Add(_Rpara_duration); |
||||||
|
parameterArrayRight.Add(_Rpara_amplitude); |
||||||
|
|
||||||
|
json_one.Add("LeftHand", parameterArrayLeft); |
||||||
|
json_one.Add("RightHand", parameterArrayRight); |
||||||
|
json_role.Add(RoleName, json_one); |
||||||
|
|
||||||
|
Debug.Log(json_role.ToJson()); |
||||||
|
_udpClient.Send(new IPEndPoint(IPAddress.Parse(IP), 8920), Encoding.UTF8.GetBytes(json_role.ToJson())); |
||||||
|
} |
||||||
|
|
||||||
|
private void OnDestroy() |
||||||
|
{ |
||||||
|
_udpClient.Received -= ReceiveMsg; |
||||||
|
_udpClient.Stop(); |
||||||
|
_udpClient.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 6c9e49f2197d3b04bbaff9d59805c939 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
|
||||||
|
public struct SingleVirbator |
||||||
|
{ |
||||||
|
public int ActiveCommand; |
||||||
|
public int Duration; |
||||||
|
public int Amplitude; |
||||||
|
|
||||||
|
public SingleVirbator(int command = 1, int duration = 0, int amplitude = 4) |
||||||
|
{ |
||||||
|
command = command < 1 ? 1 : command > 3 ? 3 : command; |
||||||
|
duration = duration < 0 ? 0 : duration; |
||||||
|
amplitude = amplitude < 4 ? 4 : amplitude > 10 ? 10 : amplitude; |
||||||
|
|
||||||
|
ActiveCommand = command; |
||||||
|
Duration = duration; |
||||||
|
Amplitude = amplitude; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class VibrationData |
||||||
|
{ |
||||||
|
public SingleVirbator[] Virbators = new SingleVirbator[2]; |
||||||
|
|
||||||
|
public VibrationData(SingleVirbator[] virbators) |
||||||
|
{ |
||||||
|
if (virbators.Length != 2) return; |
||||||
|
|
||||||
|
for (int i = 0; i < virbators.Length; i++) |
||||||
|
{ |
||||||
|
Virbators[i] = virbators[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 79059d582b396c94c927234aff494dc7 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: f191f5c21b136254a8b7594c24052059 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: c49626a0949c3364db92b0af1d01d2de |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 5a8fd747de05f8349add6d5262cac52f |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 27eb0f7a4e47fd4448882aa3616aed04 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 8de84d2e90495cf499b28c64b9cc76bf |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,438 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 内存池 |
||||||
|
/// </summary> |
||||||
|
//[IntelligentCoder.AsyncMethodPoster(Flags = IntelligentCoder.MemberFlags.Public)] |
||||||
|
public partial class BytePool |
||||||
|
{ |
||||||
|
private readonly ConcurrentDictionary<long, BytesQueue> bytesDictionary = new ConcurrentDictionary<long, BytesQueue>(); |
||||||
|
private readonly Timer m_timer; |
||||||
|
private long m_fullSize; |
||||||
|
private long m_maxSize; |
||||||
|
|
||||||
|
static BytePool() |
||||||
|
{ |
||||||
|
Default = new BytePool(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 内存池 |
||||||
|
/// </summary> |
||||||
|
public BytePool() |
||||||
|
{ |
||||||
|
m_timer = new Timer((o) => |
||||||
|
{ |
||||||
|
Clear(); |
||||||
|
}, null, 1000 * 60 * 60, 1000 * 60 * 60); |
||||||
|
KeyCapacity = 100; |
||||||
|
AutoZero = false; |
||||||
|
m_maxSize = 1024 * 1024 * 512; |
||||||
|
SetBlockSize(1024, 1024 * 1024 * 20); |
||||||
|
AddSizeKey(10240); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 默认的内存池实例 |
||||||
|
/// </summary> |
||||||
|
public static BytePool Default { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 回收内存时,自动归零 |
||||||
|
/// </summary> |
||||||
|
public bool AutoZero { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 键容量 |
||||||
|
/// </summary> |
||||||
|
public int KeyCapacity { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 单个块最大值 |
||||||
|
/// </summary> |
||||||
|
public int MaxBlockSize { get; private set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 允许的内存池最大值 |
||||||
|
/// </summary> |
||||||
|
public long MaxSize |
||||||
|
{ |
||||||
|
get => m_maxSize; |
||||||
|
set |
||||||
|
{ |
||||||
|
if (value < 1024) |
||||||
|
{ |
||||||
|
value = 1024; |
||||||
|
} |
||||||
|
m_maxSize = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 单个块最小值 |
||||||
|
/// </summary> |
||||||
|
public int MinBlockSize { get; private set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 添加尺寸键 |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool AddSizeKey(int byteSize) |
||||||
|
{ |
||||||
|
if (bytesDictionary.TryAdd(byteSize, new BytesQueue(byteSize))) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清理 |
||||||
|
/// </summary> |
||||||
|
public void Clear() |
||||||
|
{ |
||||||
|
bytesDictionary.Clear(); |
||||||
|
GC.Collect(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 确定是否包含指定尺寸键 |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool ContainsSizeKey(int byteSize) |
||||||
|
{ |
||||||
|
return bytesDictionary.ContainsKey(byteSize); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取所以内存键 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public long[] GetAllSizeKeys() |
||||||
|
{ |
||||||
|
return bytesDictionary.Keys.ToArray(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取ByteBlock |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize">长度</param> |
||||||
|
/// <param name="equalSize">要求长度相同</param> |
||||||
|
/// <returns></returns> |
||||||
|
public ByteBlock GetByteBlock(int byteSize, bool equalSize) |
||||||
|
{ |
||||||
|
return new ByteBlock(byteSize, equalSize); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取ByteBlock |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public ByteBlock GetByteBlock(int byteSize) |
||||||
|
{ |
||||||
|
return new ByteBlock(byteSize, false); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取内存核心。获取的核心可以不用归还。 |
||||||
|
/// 如果要调用<see cref="Recycle(byte[])"/>归还,切记不要有持久性引用。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <param name="equalSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public byte[] GetByteCore(int byteSize, bool equalSize = false) |
||||||
|
{ |
||||||
|
BytesQueue bytesCollection; |
||||||
|
if (equalSize) |
||||||
|
{ |
||||||
|
//等长 |
||||||
|
if (bytesDictionary.TryGetValue(byteSize, out bytesCollection)) |
||||||
|
{ |
||||||
|
if (bytesCollection.TryGet(out byte[] bytes)) |
||||||
|
{ |
||||||
|
m_fullSize -= byteSize; |
||||||
|
return bytes; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CheckKeyCapacity(byteSize); |
||||||
|
} |
||||||
|
return new byte[byteSize]; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
//byteSize = HitSize(byteSize); |
||||||
|
//byteSize = byteSize; |
||||||
|
//搜索已创建集合 |
||||||
|
if (bytesDictionary.TryGetValue(byteSize, out bytesCollection)) |
||||||
|
{ |
||||||
|
if (bytesCollection.TryGet(out byte[] bytes)) |
||||||
|
{ |
||||||
|
m_fullSize -= byteSize; |
||||||
|
return bytes; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
CheckKeyCapacity(byteSize); |
||||||
|
} |
||||||
|
return new byte[byteSize]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取内存池容量 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public long GetPoolSize() |
||||||
|
{ |
||||||
|
long size = 0; |
||||||
|
foreach (var item in bytesDictionary.Values) |
||||||
|
{ |
||||||
|
size += item.FullSize; |
||||||
|
} |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取ValueByteBlock |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <param name="equalSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
//[IntelligentCoder.AsyncMethodIgnore] |
||||||
|
public ValueByteBlock GetValueByteBlock(int byteSize, bool equalSize) |
||||||
|
{ |
||||||
|
return new ValueByteBlock(byteSize, equalSize); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取ValueByteBlock |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
//[IntelligentCoder.AsyncMethodIgnore] |
||||||
|
public ValueByteBlock GetValueByteBlock(int byteSize) |
||||||
|
{ |
||||||
|
return new ValueByteBlock(byteSize, false); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 回收内存核心。 |
||||||
|
/// <para>注意:回收的内存,必须百分百确定该对象没有再被其他引用。不然这属于危险操作。</para> |
||||||
|
/// </summary> |
||||||
|
/// <param name="bytes"></param> |
||||||
|
public void Recycle(byte[] bytes) |
||||||
|
{ |
||||||
|
if (bytes == null || bytes.Length > MaxBlockSize || bytes.Length < MinBlockSize) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
if (m_maxSize > m_fullSize) |
||||||
|
{ |
||||||
|
if (bytesDictionary.TryGetValue(bytes.Length, out BytesQueue bytesQueue)) |
||||||
|
{ |
||||||
|
if (AutoZero) |
||||||
|
{ |
||||||
|
Array.Clear(bytes, 0, bytes.Length); |
||||||
|
} |
||||||
|
m_fullSize += bytes.Length; |
||||||
|
bytesQueue.Add(bytes); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
long size = 0; |
||||||
|
foreach (var collection in bytesDictionary.Values) |
||||||
|
{ |
||||||
|
size += collection.FullSize; |
||||||
|
} |
||||||
|
m_fullSize = size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 移除尺寸键 |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteSize"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool RemoveSizeKey(int byteSize) |
||||||
|
{ |
||||||
|
if (bytesDictionary.TryRemove(byteSize, out BytesQueue queue)) |
||||||
|
{ |
||||||
|
queue.Clear(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设置内存块参数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="minBlockSize"></param> |
||||||
|
/// <param name="maxBlockSize"></param> |
||||||
|
public void SetBlockSize(int minBlockSize, int maxBlockSize) |
||||||
|
{ |
||||||
|
this.MaxBlockSize = maxBlockSize; |
||||||
|
this.MinBlockSize = minBlockSize; |
||||||
|
bytesDictionary.Clear(); |
||||||
|
} |
||||||
|
|
||||||
|
private void CheckKeyCapacity(int byteSize) |
||||||
|
{ |
||||||
|
if (byteSize < MinBlockSize || byteSize > MaxBlockSize) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
if (bytesDictionary.Count < KeyCapacity) |
||||||
|
{ |
||||||
|
bytesDictionary.TryAdd(byteSize, new BytesQueue(byteSize)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
List<BytesQueue> bytesQueues = bytesDictionary.Values.ToList(); |
||||||
|
bytesQueues.Sort((x, y) => { return x.m_referenced > y.m_referenced ? -1 : 1; }); |
||||||
|
for (int i = (int)(bytesQueues.Count * 0.2); i < bytesQueues.Count; i++) |
||||||
|
{ |
||||||
|
if (bytesDictionary.TryRemove(bytesQueues[i].m_size, out BytesQueue queue)) |
||||||
|
{ |
||||||
|
queue.Clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||||
|
private int HitSize(int num) |
||||||
|
{ |
||||||
|
if (num < MinBlockSize) |
||||||
|
{ |
||||||
|
num = MinBlockSize; |
||||||
|
} |
||||||
|
|
||||||
|
if (num <= 10240)//10k |
||||||
|
{ |
||||||
|
return 10240; |
||||||
|
} |
||||||
|
else if (num <= 65536)//64k |
||||||
|
{ |
||||||
|
return 65536; |
||||||
|
} |
||||||
|
else if (num <= 102400)//100k |
||||||
|
{ |
||||||
|
return 102400; |
||||||
|
} |
||||||
|
else if (num <= 524288) //512k |
||||||
|
{ |
||||||
|
return 524288; |
||||||
|
} |
||||||
|
else if (num <= 1048576)//1Mb |
||||||
|
{ |
||||||
|
return 1048576; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 2)//2Mb |
||||||
|
{ |
||||||
|
return 1048576 * 2; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 3)//3Mb |
||||||
|
{ |
||||||
|
return 1048576 * 3; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 4)//4Mb |
||||||
|
{ |
||||||
|
return 1048576 * 4; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 5)//5Mb |
||||||
|
{ |
||||||
|
return 1048576 * 5; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 6)//6Mb |
||||||
|
{ |
||||||
|
return 1048576 * 6; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 7)//7Mb |
||||||
|
{ |
||||||
|
return 1048576 * 7; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 8)//8Mb |
||||||
|
{ |
||||||
|
return 1048576 * 8; |
||||||
|
} |
||||||
|
else if (num <= 1048576 * 9)//9Mb |
||||||
|
{ |
||||||
|
return 1048576 * 9; |
||||||
|
} |
||||||
|
else if (num <= 10485760)//10Mb |
||||||
|
{ |
||||||
|
return 10485760; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 12)//12Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 12; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 15)//15Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 15; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 18)//18Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 18; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 20)//20Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 20; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 30)//30Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 30; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 40)//40Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 40; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 50)//50Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 50; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 100)//100Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 100; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 500)//500Mb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 500; |
||||||
|
} |
||||||
|
else if (num <= 1024 * 1024 * 1024)//1Gb |
||||||
|
{ |
||||||
|
return 1024 * 1024 * 1024; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return num; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 553857cfe781f5b428c510f4b08f01d9 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Diagnostics; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 字节块集合 |
||||||
|
/// </summary> |
||||||
|
[DebuggerDisplay("Count = {bytesQueue.Count}")] |
||||||
|
internal class BytesQueue: ConcurrentStack<byte[]> |
||||||
|
{ |
||||||
|
internal int m_size; |
||||||
|
|
||||||
|
internal BytesQueue(int size) |
||||||
|
{ |
||||||
|
m_size = size; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 占用空间 |
||||||
|
/// </summary> |
||||||
|
public long FullSize => m_size * this.Count; |
||||||
|
|
||||||
|
internal long m_referenced; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取当前实例中的空闲的Block |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryGet(out byte[] bytes) |
||||||
|
{ |
||||||
|
m_referenced++; |
||||||
|
return base.TryPop(out bytes); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 向当前集合添加Block |
||||||
|
/// </summary> |
||||||
|
/// <param name="bytes"></param> |
||||||
|
public void Add(byte[] bytes) |
||||||
|
{ |
||||||
|
base.Push(bytes); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 51ffc67d55445a64493a7b364bcaa18a |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 85147b475bda37e4e9772ae01bc06ddc |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 11becbbbdd502004f9f9f31f1d46d1b0 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 缓存实体 |
||||||
|
/// </summary> |
||||||
|
public class CacheEntry<TKey, TValue> : ICacheEntry<TKey, TValue> |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 缓存实体 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
public CacheEntry(TKey key) : this(key, default) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 缓存实体 |
||||||
|
/// </summary> |
||||||
|
public CacheEntry(TKey key, TValue value) |
||||||
|
{ |
||||||
|
UpdateTime = DateTime.Now; |
||||||
|
Duration = TimeSpan.FromSeconds(60); |
||||||
|
Key = key; |
||||||
|
Value = value; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 有效区间。如果想长期有效,请使用<see cref="TimeSpan.Zero"/> |
||||||
|
/// </summary> |
||||||
|
public TimeSpan Duration { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 键 |
||||||
|
/// </summary> |
||||||
|
public TKey Key { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 更新时间 |
||||||
|
/// </summary> |
||||||
|
public DateTime UpdateTime { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 值 |
||||||
|
/// </summary> |
||||||
|
public TValue Value { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 9d02ae7f7391f494e8d17bd8bf67f1d9 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// CacheExtensions |
||||||
|
/// </summary> |
||||||
|
public static class CacheManagementExtensions |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="ICache{TKey, TValue}.AddCache(ICacheEntry{TKey, TValue})"/> |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="TKey"></typeparam> |
||||||
|
/// <typeparam name="TValue"></typeparam> |
||||||
|
/// <param name="cacheManagement"></param> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <param name="duration"></param> |
||||||
|
public static void AddCache<TKey, TValue>(this ICache<TKey, TValue> cacheManagement, TKey key, TValue value, int duration = 60 * 1000) |
||||||
|
{ |
||||||
|
cacheManagement.AddCache(new CacheEntry<TKey, TValue>(key) |
||||||
|
{ |
||||||
|
Value = value, |
||||||
|
Duration = TimeSpan.FromMilliseconds(duration) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="ICache{TKey, TValue}.SetCache(ICacheEntry{TKey, TValue})"/> |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="TKey"></typeparam> |
||||||
|
/// <typeparam name="TValue"></typeparam> |
||||||
|
/// <param name="cacheManagement"></param> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <param name="duration"></param> |
||||||
|
public static void SetCache<TKey, TValue>(this ICache<TKey, TValue> cacheManagement, TKey key, TValue value, int duration = 60 * 1000) |
||||||
|
{ |
||||||
|
cacheManagement.SetCache(new CacheEntry<TKey, TValue>(key) |
||||||
|
{ |
||||||
|
Value = value, |
||||||
|
Duration = TimeSpan.FromMilliseconds(duration) |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: ec28cc368625dcc4e9f8dd9b50d19248 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,106 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 缓存键值 |
||||||
|
/// </summary> |
||||||
|
//[IntelligentCoder.AsyncMethodPoster(Flags = IntelligentCoder.MemberFlags.Public)] |
||||||
|
public partial interface ICache<TKey, TValue> |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 添加缓存。当缓存存在时,不会添加成功。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity">缓存实体</param> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
bool AddCache(ICacheEntry<TKey, TValue> entity); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清空所有缓存 |
||||||
|
/// </summary> |
||||||
|
void ClearCache(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清空所有缓存 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
Task ClearCacheAsync(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 判断缓存是否存在,且在生命周期内。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
bool ContainsCache(TKey key); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 判断缓存是否存在,且在生命周期内。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
Task<bool> ContainsCacheAsync(TKey key); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设置缓存,不管缓存存不存在,都会添加。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity"></param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
bool SetCache(ICacheEntry<TKey, TValue> entity); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 设置缓存,不管缓存存不存在,都会添加。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity"></param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
Task<bool> SetCacheAsync(ICacheEntry<TKey, TValue> entity); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取指定键的缓存。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key">键</param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
ICacheEntry<TKey, TValue> GetCache(TKey key); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取指定键的缓存。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key">键</param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
Task<ICacheEntry<TKey, TValue>> GetCacheAsync(TKey key); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 移除指定键的缓存。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key">键</param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
bool RemoveCache(TKey key); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 移除指定键的缓存。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key">键</param> |
||||||
|
/// <returns></returns> |
||||||
|
/// <exception cref="ArgumentNullException"></exception> |
||||||
|
Task<bool> RemoveCacheAsync(TKey key); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 6ed4b3ccd0164f14cbeaa9ad36ce91a0 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 缓存实体接口 |
||||||
|
/// </summary> |
||||||
|
public interface ICacheEntry |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 有效区间。如果想长期有效,请使用<see cref="TimeSpan.Zero"/> |
||||||
|
/// </summary> |
||||||
|
public TimeSpan Duration { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 更新时间 |
||||||
|
/// </summary> |
||||||
|
public DateTime UpdateTime { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 缓存实体接口 |
||||||
|
/// </summary> |
||||||
|
public interface ICacheEntry<out TKey, TValue> : ICacheEntry |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 键 |
||||||
|
/// </summary> |
||||||
|
public TKey Key { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 值 |
||||||
|
/// </summary> |
||||||
|
public TValue Value { get; set; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: e5c866ce47c95944f86bbb341fe5bc19 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,319 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Collections; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 一个简单的内存缓存 |
||||||
|
/// </summary> |
||||||
|
public class MemoryCache<TKey, TValue> : IEnumerable<ICacheEntry<TKey, TValue>>, ICache<TKey, TValue> |
||||||
|
{ |
||||||
|
private readonly ConcurrentDictionary<TKey, ICacheEntry<TKey, TValue>> m_pairs = new ConcurrentDictionary<TKey, ICacheEntry<TKey, TValue>>(); |
||||||
|
private readonly Timer m_timer; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 一个简单的内存缓存 |
||||||
|
/// </summary> |
||||||
|
public MemoryCache() |
||||||
|
{ |
||||||
|
m_timer = new Timer((o) => |
||||||
|
{ |
||||||
|
List<TKey> list = new List<TKey>(); |
||||||
|
foreach (var item in m_pairs) |
||||||
|
{ |
||||||
|
if (DateTime.Now - item.Value.UpdateTime > item.Value.Duration) |
||||||
|
{ |
||||||
|
list.Add(item.Key); |
||||||
|
} |
||||||
|
} |
||||||
|
foreach (var item in list) |
||||||
|
{ |
||||||
|
OnRemove(item, out _); |
||||||
|
} |
||||||
|
}, null, 0, 60 * 1000); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 当每个元素超时被移除时触发。 |
||||||
|
/// </summary> |
||||||
|
public Action<ICacheEntry<TKey, TValue>> Remove { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity"></param> |
||||||
|
public bool AddCache(ICacheEntry<TKey, TValue> entity) |
||||||
|
{ |
||||||
|
return m_pairs.TryAdd(entity.Key, entity); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public Task<bool> AddCacheAsync(ICacheEntry<TKey, TValue> entity) |
||||||
|
{ |
||||||
|
return EasyTask.Run(() => |
||||||
|
{ |
||||||
|
return AddCache(entity); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清空所有缓存 |
||||||
|
/// </summary> |
||||||
|
public void ClearCache() |
||||||
|
{ |
||||||
|
m_pairs.Clear(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public Task ClearCacheAsync() |
||||||
|
{ |
||||||
|
return EasyTask.Run(() => |
||||||
|
{ |
||||||
|
ClearCache(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool ContainsCache(TKey key) |
||||||
|
{ |
||||||
|
if (m_pairs.TryGetValue(key, out ICacheEntry<TKey, TValue> cache)) |
||||||
|
{ |
||||||
|
if (cache.Duration == TimeSpan.Zero) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (DateTime.Now - cache.UpdateTime > cache.Duration) |
||||||
|
{ |
||||||
|
OnRemove(key, out _); |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public Task<bool> ContainsCacheAsync(TKey key) |
||||||
|
{ |
||||||
|
return EasyTask.Run(() => |
||||||
|
{ |
||||||
|
return ContainsCache(key); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取缓存实体。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public ICacheEntry<TKey, TValue> GetCache(TKey key) |
||||||
|
{ |
||||||
|
ICacheEntry<TKey, TValue> cache; |
||||||
|
if (m_pairs.TryGetValue(key, out cache)) |
||||||
|
{ |
||||||
|
if (cache.Duration == TimeSpan.Zero) |
||||||
|
{ |
||||||
|
return cache; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (DateTime.Now - cache.UpdateTime > cache.Duration) |
||||||
|
{ |
||||||
|
OnRemove(key, out _); |
||||||
|
return default; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return cache; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return default; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public Task<ICacheEntry<TKey, TValue>> GetCacheAsync(TKey key) |
||||||
|
{ |
||||||
|
return EasyTask.Run(() => |
||||||
|
{ |
||||||
|
return GetCache(key); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public IEnumerator<ICacheEntry<TKey, TValue>> GetEnumerator() |
||||||
|
{ |
||||||
|
return m_pairs.Values.GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||||
|
{ |
||||||
|
return GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 移除缓存 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="entity"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool RemoveCache(TKey key, out ICacheEntry<TKey, TValue> entity) |
||||||
|
{ |
||||||
|
return OnRemove(key, out entity); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 移除缓存 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool RemoveCache(TKey key) |
||||||
|
{ |
||||||
|
return OnRemove(key, out _); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public Task<bool> RemoveCacheAsync(TKey key) |
||||||
|
{ |
||||||
|
return EasyTask.Run(() => |
||||||
|
{ |
||||||
|
return RemoveCache(key); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool SetCache(ICacheEntry<TKey, TValue> entity) |
||||||
|
{ |
||||||
|
m_pairs.AddOrUpdate(entity.Key, entity, (k, v) => |
||||||
|
{ |
||||||
|
return entity; |
||||||
|
}); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="entity"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public Task<bool> SetCacheAsync(ICacheEntry<TKey, TValue> entity) |
||||||
|
{ |
||||||
|
return EasyTask.Run(() => |
||||||
|
{ |
||||||
|
return SetCache(entity); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取对应的值。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <param name="update"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryGetValue(TKey key, out TValue value, bool update = false) |
||||||
|
{ |
||||||
|
if (m_pairs.TryGetValue(key, out ICacheEntry<TKey, TValue> cache)) |
||||||
|
{ |
||||||
|
if (cache.Duration == TimeSpan.Zero) |
||||||
|
{ |
||||||
|
if (update) |
||||||
|
{ |
||||||
|
cache.UpdateTime = DateTime.Now; |
||||||
|
} |
||||||
|
value = cache.Value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (DateTime.Now - cache.UpdateTime > cache.Duration) |
||||||
|
{ |
||||||
|
RemoveCache(key); |
||||||
|
value = default; |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (update) |
||||||
|
{ |
||||||
|
cache.UpdateTime = DateTime.Now; |
||||||
|
} |
||||||
|
value = (TValue)cache.Value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
value = default; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private bool OnRemove(TKey key, out ICacheEntry<TKey, TValue> cache) |
||||||
|
{ |
||||||
|
if (m_pairs.TryRemove(key, out cache)) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
Remove?.Invoke(cache); |
||||||
|
return true; |
||||||
|
} |
||||||
|
catch |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: e633e6ed48a4696408871330bd156a4b |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 3df138696b1ee8243ad05ade1127ab1e |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 53932f7bd6e1bbb4fa61b95a87a534db |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,125 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System.Collections.Concurrent; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 安全双向字典 |
||||||
|
/// </summary> |
||||||
|
public class ConcurrentDoublyDictionary<TKey, TValue> |
||||||
|
{ |
||||||
|
private readonly ConcurrentDictionary<TKey, TValue> m_keyToValue; |
||||||
|
private readonly ConcurrentDictionary<TValue, TKey> m_valueToKey; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
public ConcurrentDoublyDictionary() |
||||||
|
{ |
||||||
|
m_keyToValue = new ConcurrentDictionary<TKey, TValue>(); |
||||||
|
m_valueToKey = new ConcurrentDictionary<TValue, TKey>(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由键指向值得集合 |
||||||
|
/// </summary> |
||||||
|
public ConcurrentDictionary<TKey, TValue> KeyToValue => m_keyToValue; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由值指向键的集合 |
||||||
|
/// </summary> |
||||||
|
public ConcurrentDictionary<TValue, TKey> ValueToKey => m_valueToKey; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 尝试将指定的键和值添加到字典中。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryAdd(TKey key, TValue value) |
||||||
|
{ |
||||||
|
if (m_keyToValue.TryAdd(key, value)) |
||||||
|
{ |
||||||
|
if (m_valueToKey.TryAdd(value, key)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
m_keyToValue.TryRemove(key, out _); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由键尝试移除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryRemoveFromKey(TKey key, out TValue value) |
||||||
|
{ |
||||||
|
if (m_keyToValue.TryRemove(key, out value)) |
||||||
|
{ |
||||||
|
if (m_valueToKey.TryRemove(value, out _)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由值尝试移除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryRemoveFromValue(TValue value, out TKey key) |
||||||
|
{ |
||||||
|
if (m_valueToKey.TryRemove(value, out key)) |
||||||
|
{ |
||||||
|
if (m_keyToValue.TryRemove(key, out _)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由键获取到值 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryGetFromKey(TKey key, out TValue value) |
||||||
|
{ |
||||||
|
return m_keyToValue.TryGetValue(key, out value); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由值获取到键 |
||||||
|
/// </summary> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryGetFromValue(TValue value, out TKey key) |
||||||
|
{ |
||||||
|
return m_valueToKey.TryGetValue(value, out key); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 83a259826b7d3e040a92f0f4b0f2b43d |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,680 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Collections; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 线程安全的List,其基本操作和List一致。 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="T"></typeparam> |
||||||
|
public class ConcurrentList<T> : IList<T> |
||||||
|
{ |
||||||
|
private readonly List<T> m_list; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="collection"></param> |
||||||
|
public ConcurrentList(IEnumerable<T> collection) |
||||||
|
{ |
||||||
|
m_list = new List<T>(collection); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
public ConcurrentList() |
||||||
|
{ |
||||||
|
m_list = new List<T>(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="capacity"></param> |
||||||
|
public ConcurrentList(int capacity) |
||||||
|
{ |
||||||
|
m_list = new List<T>(capacity); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 元素数量 |
||||||
|
/// </summary> |
||||||
|
public int Count |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.Count; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 是否为只读 |
||||||
|
/// </summary> |
||||||
|
public bool IsReadOnly => false; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取索引元素 |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public T this[int index] |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list[index]; |
||||||
|
} |
||||||
|
} |
||||||
|
set |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list[index] = value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 添加元素 |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
public void Add(T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Add(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清空所有元素 |
||||||
|
/// </summary> |
||||||
|
public void Clear() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 是否包含某个元素 |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool Contains(T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.Contains(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 复制到 |
||||||
|
/// </summary> |
||||||
|
/// <param name="array"></param> |
||||||
|
/// <param name="arrayIndex"></param> |
||||||
|
public void CopyTo(T[] array, int arrayIndex) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.CopyTo(array, arrayIndex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 返回迭代器 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public IEnumerator<T> GetEnumerator() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.ToList().GetEnumerator(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 返回迭代器组合 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return GetEnumerator(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 索引 |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int IndexOf(T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.IndexOf(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 插入 |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="item"></param> |
||||||
|
public void Insert(int index, T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Insert(index, item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 移除元素 |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool Remove(T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.Remove(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 按索引移除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
public void RemoveAt(int index) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
if (index < m_list.Count) |
||||||
|
{ |
||||||
|
m_list.RemoveAt(index); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取或设置容量 |
||||||
|
/// </summary> |
||||||
|
public int Capacity |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.Capacity; |
||||||
|
} |
||||||
|
} |
||||||
|
set |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Capacity = value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.AddRange(IEnumerable{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="collection"></param> |
||||||
|
public void AddRange(IEnumerable<T> collection) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.AddRange(collection); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.BinarySearch(T)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int BinarySearch(T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.BinarySearch(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.BinarySearch(T, IComparer{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <param name="comparer"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int BinarySearch(T item, IComparer<T> comparer) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.BinarySearch(item, comparer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.BinarySearch(int, int, T, IComparer{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <param name="comparer"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int BinarySearch(int index, int count, T item, IComparer<T> comparer) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.BinarySearch(index, count, item, comparer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.ConvertAll{TOutput}(Converter{T, TOutput})"/> |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="TOutput"></typeparam> |
||||||
|
/// <param name="converter"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.ConvertAll(converter); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Find(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public T Find(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.Find(match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindAll(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public List<T> FindAll(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindAll(match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindIndex(int, int, Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="startIndex"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int FindIndex(int startIndex, int count, Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindIndex(startIndex, count, match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindIndex(int, Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="startIndex"></param> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int FindIndex(int startIndex, Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindIndex(startIndex, match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindIndex(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int FindIndex(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindIndex(match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindLast(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public T FindLast(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindLast(match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindLastIndex(int, int, Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="startIndex"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int FindLastIndex(int startIndex, int count, Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindLastIndex(startIndex, count, match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindLastIndex(int, Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="startIndex"></param> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int FindLastIndex(int startIndex, Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindLastIndex(startIndex, match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.FindLastIndex(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int FindLastIndex(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.FindLastIndex(match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.ForEach(Action{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="action"></param> |
||||||
|
public void ForEach(Action<T> action) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.ForEach(action); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.GetRange(int, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public List<T> GetRange(int index, int count) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.GetRange(index, count); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.IndexOf(T, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int IndexOf(T item, int index) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.IndexOf(item, index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.IndexOf(T, int, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int IndexOf(T item, int index, int count) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.IndexOf(item, index, count); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.InsertRange(int, IEnumerable{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="collection"></param> |
||||||
|
public void InsertRange(int index, IEnumerable<T> collection) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.InsertRange(index, collection); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.LastIndexOf(T)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int LastIndexOf(T item) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.IndexOf(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.LastIndexOf(T, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int LastIndexOf(T item, int index) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.LastIndexOf(item, index); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.LastIndexOf(T, int, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public int LastIndexOf(T item, int index, int count) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.LastIndexOf(item, index, count); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.RemoveAll(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
public void RemoveAll(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.RemoveAll(match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.RemoveRange(int, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
public void RemoveRange(int index, int count) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.RemoveRange(index, count); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Reverse()"/> |
||||||
|
/// </summary> |
||||||
|
public void Reverse() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Reverse(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Reverse(int, int)"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
public void Reverse(int index, int count) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Reverse(index, count); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Sort()"/> |
||||||
|
/// </summary> |
||||||
|
public void Sort() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Sort(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Sort(Comparison{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="comparison"></param> |
||||||
|
public void Sort(Comparison<T> comparison) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Sort(comparison); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Sort(IComparer{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="comparer"></param> |
||||||
|
public void Sort(IComparer<T> comparer) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Sort(comparer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.Sort(int, int, IComparer{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="index"></param> |
||||||
|
/// <param name="count"></param> |
||||||
|
/// <param name="comparer"></param> |
||||||
|
public void Sort(int index, int count, IComparer<T> comparer) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.Sort(index, count, comparer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.ToArray"/> |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public T[] ToArray() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.ToArray(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.TrimExcess"/> |
||||||
|
/// </summary> |
||||||
|
public void TrimExcess() |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
m_list.TrimExcess(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// <inheritdoc cref="List{T}.TrueForAll(Predicate{T})"/> |
||||||
|
/// </summary> |
||||||
|
/// <param name="match"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TrueForAll(Predicate<T> match) |
||||||
|
{ |
||||||
|
lock (((ICollection)m_list).SyncRoot) |
||||||
|
{ |
||||||
|
return m_list.TrueForAll(match); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 7b1dd8fbd71ec3648a077c327be14d66 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,194 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System.Collections.Concurrent; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 三元组合 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="TKey1"></typeparam> |
||||||
|
/// <typeparam name="TKey2"></typeparam> |
||||||
|
/// <typeparam name="TValue"></typeparam> |
||||||
|
public readonly struct Ternary<TKey1, TKey2, TValue> |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 三元组合 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key1"></param> |
||||||
|
/// <param name="key2"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
public Ternary(TKey1 key1, TKey2 key2, TValue value) |
||||||
|
{ |
||||||
|
Key1 = key1; |
||||||
|
Key2 = key2; |
||||||
|
Value = value; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 首键 |
||||||
|
/// </summary> |
||||||
|
public readonly TKey1 Key1 { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 次键 |
||||||
|
/// </summary> |
||||||
|
public readonly TKey2 Key2 { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 值 |
||||||
|
/// </summary> |
||||||
|
public readonly TValue Value { get; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 线程安全的双键字典 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="TKey1"></typeparam> |
||||||
|
/// <typeparam name="TKey2"></typeparam> |
||||||
|
/// <typeparam name="TValue"></typeparam> |
||||||
|
public class ConcurrentMultiDictionary<TKey1, TKey2, TValue> |
||||||
|
{ |
||||||
|
private readonly ConcurrentDictionary<TKey1, Ternary<TKey1, TKey2, TValue>> m_key1ToValue = |
||||||
|
new ConcurrentDictionary<TKey1, Ternary<TKey1, TKey2, TValue>>(); |
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<TKey2, Ternary<TKey1, TKey2, TValue>> m_key2ToValue = |
||||||
|
new ConcurrentDictionary<TKey2, Ternary<TKey1, TKey2, TValue>>(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 元素数量。 |
||||||
|
/// </summary> |
||||||
|
public int Count { get => m_key1ToValue.Count; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清空所有元素。 |
||||||
|
/// </summary> |
||||||
|
public void Clear() |
||||||
|
{ |
||||||
|
m_key1ToValue.Clear(); |
||||||
|
m_key2ToValue.Clear(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 是否包含指定键。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool ContainsKey(TKey2 key) |
||||||
|
{ |
||||||
|
return m_key2ToValue.ContainsKey(key); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 是否包含指定键。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool ContainsKey(TKey1 key) |
||||||
|
{ |
||||||
|
return m_key1ToValue.ContainsKey(key); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 尝试添加。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key1"></param> |
||||||
|
/// <param name="key2"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryAdd(TKey1 key1, TKey2 key2, TValue value) |
||||||
|
{ |
||||||
|
var ternary = new Ternary<TKey1, TKey2, TValue>(key1, key2, value); |
||||||
|
if (m_key1ToValue.TryAdd(key1, ternary) && m_key2ToValue.TryAdd(key2, ternary)) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
m_key1ToValue.TryRemove(key1, out _); |
||||||
|
m_key2ToValue.TryRemove(key2, out _); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由首键删除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryRemove(TKey1 key, out TValue value) |
||||||
|
{ |
||||||
|
if (m_key1ToValue.TryRemove(key, out var ternary)) |
||||||
|
{ |
||||||
|
m_key2ToValue.TryRemove(ternary.Key2, out _); |
||||||
|
value = ternary.Value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
value = default; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由次键删除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryRemove(TKey2 key, out TValue value) |
||||||
|
{ |
||||||
|
if (m_key2ToValue.TryRemove(key, out var ternary)) |
||||||
|
{ |
||||||
|
m_key1ToValue.TryRemove(ternary.Key1, out _); |
||||||
|
value = ternary.Value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
value = default; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由首键获取值 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryGetValue(TKey1 key, out TValue value) |
||||||
|
{ |
||||||
|
if (m_key1ToValue.TryGetValue(key, out var ternary)) |
||||||
|
{ |
||||||
|
value = ternary.Value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
value = default; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 由次键获取值 |
||||||
|
/// </summary> |
||||||
|
/// <param name="key"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool TryGetValue(TKey2 key, out TValue value) |
||||||
|
{ |
||||||
|
if (m_key2ToValue.TryGetValue(key, out var ternary)) |
||||||
|
{ |
||||||
|
value = ternary.Value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
value = default; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: eeb90554916135446803f8c76dae8512 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 智能安全队列 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="T"></typeparam> |
||||||
|
public class IntelligentConcurrentQueue<T> : ConcurrentQueue<T> |
||||||
|
{ |
||||||
|
private int m_count; |
||||||
|
|
||||||
|
private readonly int m_maxCount; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="maxCount"></param> |
||||||
|
public IntelligentConcurrentQueue(int maxCount) |
||||||
|
{ |
||||||
|
m_maxCount = maxCount; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 允许的最大长度 |
||||||
|
/// </summary> |
||||||
|
public int MaxCount => m_maxCount; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 长度 |
||||||
|
/// </summary> |
||||||
|
public new int Count => m_count; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 入队 |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
public new void Enqueue(T item) |
||||||
|
{ |
||||||
|
SpinWait.SpinUntil(Check); |
||||||
|
Interlocked.Increment(ref m_count); |
||||||
|
base.Enqueue(item); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 出队 |
||||||
|
/// </summary> |
||||||
|
/// <param name="result"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public new bool TryDequeue(out T result) |
||||||
|
{ |
||||||
|
if (base.TryDequeue(out result)) |
||||||
|
{ |
||||||
|
Interlocked.Decrement(ref m_count); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private bool Check() |
||||||
|
{ |
||||||
|
return m_count < m_maxCount; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 7c09e3313cbf00848ae51868bbf94996 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,233 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 队列数据 |
||||||
|
/// </summary> |
||||||
|
public interface IQueueData |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 数据长度 |
||||||
|
/// </summary> |
||||||
|
int Size { get; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 传输字节 |
||||||
|
/// </summary> |
||||||
|
public class QueueDataBytes : IQueueData |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="buffer"></param> |
||||||
|
/// <param name="offset"></param> |
||||||
|
/// <param name="length"></param> |
||||||
|
public QueueDataBytes(byte[] buffer, int offset, int length) |
||||||
|
{ |
||||||
|
Offset = offset; |
||||||
|
Length = length; |
||||||
|
Buffer = buffer; |
||||||
|
Size = length; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 从指定内存创建一个新对象,且内存也为新创建。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="buffer"></param> |
||||||
|
/// <param name="offset"></param> |
||||||
|
/// <param name="length"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public static QueueDataBytes CreateNew(byte[] buffer, int offset, int length) |
||||||
|
{ |
||||||
|
byte[] buf = new byte[length]; |
||||||
|
Array.Copy(buffer, offset, buf, 0, length); |
||||||
|
return new QueueDataBytes(buf); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="buffer"></param> |
||||||
|
public QueueDataBytes(byte[] buffer) : this(buffer, 0, buffer.Length) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 数据内存 |
||||||
|
/// </summary> |
||||||
|
public byte[] Buffer { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 长度 |
||||||
|
/// </summary> |
||||||
|
public int Length { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 偏移 |
||||||
|
/// </summary> |
||||||
|
public int Offset { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 尺寸 |
||||||
|
/// </summary> |
||||||
|
public int Size { get; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 智能数据安全队列 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="T"></typeparam> |
||||||
|
public class IntelligentDataQueue<T> : ConcurrentQueue<T> where T : IQueueData |
||||||
|
{ |
||||||
|
private long m_actualSize; |
||||||
|
private bool m_free; |
||||||
|
private long m_maxSize; |
||||||
|
private Action<bool> m_onQueueChanged; |
||||||
|
private bool m_overflowWait; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="maxSize"></param> |
||||||
|
public IntelligentDataQueue(long maxSize) |
||||||
|
{ |
||||||
|
m_free = true; |
||||||
|
m_overflowWait = true; |
||||||
|
MaxSize = maxSize; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
public IntelligentDataQueue() : this(1024 * 1024 * 10) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 实际尺寸 |
||||||
|
/// </summary> |
||||||
|
public long ActualSize => m_actualSize; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 是否有空位允许入队 |
||||||
|
/// </summary> |
||||||
|
public bool Free => m_free; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 允许的最大长度 |
||||||
|
/// </summary> |
||||||
|
public long MaxSize |
||||||
|
{ |
||||||
|
get => m_maxSize; |
||||||
|
set |
||||||
|
{ |
||||||
|
if (value < 1) |
||||||
|
{ |
||||||
|
value = 1; |
||||||
|
} |
||||||
|
m_maxSize = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 在队列修改时 |
||||||
|
/// </summary> |
||||||
|
public Action<bool> OnQueueChanged |
||||||
|
{ |
||||||
|
get => m_onQueueChanged; |
||||||
|
set => m_onQueueChanged = value; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 溢出等待 |
||||||
|
/// </summary> |
||||||
|
public bool OverflowWait |
||||||
|
{ |
||||||
|
get => m_overflowWait; |
||||||
|
set => m_overflowWait = value; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 超时时间。默认1000*30ms; |
||||||
|
/// </summary> |
||||||
|
public int Timeout { get; set; } = 1000 * 30; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 清空队列 |
||||||
|
/// </summary> |
||||||
|
public void Clear(Action<T> onClear) |
||||||
|
{ |
||||||
|
while (base.TryDequeue(out T t)) |
||||||
|
{ |
||||||
|
onClear?.Invoke(t); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 入队 |
||||||
|
/// </summary> |
||||||
|
/// <param name="item"></param> |
||||||
|
public new void Enqueue(T item) |
||||||
|
{ |
||||||
|
lock (this) |
||||||
|
{ |
||||||
|
bool free = m_actualSize < m_maxSize; |
||||||
|
if (m_free != free) |
||||||
|
{ |
||||||
|
m_free = free; |
||||||
|
m_onQueueChanged?.Invoke(m_free); |
||||||
|
} |
||||||
|
|
||||||
|
if (m_overflowWait) |
||||||
|
{ |
||||||
|
SpinWait.SpinUntil(Check, Timeout); |
||||||
|
} |
||||||
|
|
||||||
|
Interlocked.Add(ref m_actualSize, item.Size); |
||||||
|
base.Enqueue(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 出队 |
||||||
|
/// </summary> |
||||||
|
/// <param name="result"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public new bool TryDequeue(out T result) |
||||||
|
{ |
||||||
|
if (base.TryDequeue(out result)) |
||||||
|
{ |
||||||
|
Interlocked.Add(ref m_actualSize, -result.Size); |
||||||
|
bool free = m_actualSize < m_maxSize; |
||||||
|
if (m_free != free) |
||||||
|
{ |
||||||
|
m_free = free; |
||||||
|
m_onQueueChanged?.Invoke(m_free); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private bool Check() |
||||||
|
{ |
||||||
|
return m_actualSize < m_maxSize; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 3526776f15c67ce4783225d603528c08 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,150 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 触发器队列 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="T"></typeparam> |
||||||
|
public class TriggerQueue<T> : DisposableObject |
||||||
|
{ |
||||||
|
private readonly ReaderWriterLockSlim m_lockSlim; |
||||||
|
private readonly ConcurrentQueue<T> m_queue; |
||||||
|
private readonly Timer m_timer; |
||||||
|
private volatile bool m_sending; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 触发器队列 |
||||||
|
/// </summary> |
||||||
|
public TriggerQueue() |
||||||
|
{ |
||||||
|
m_lockSlim = new ReaderWriterLockSlim(); |
||||||
|
m_queue = new ConcurrentQueue<T>(); |
||||||
|
m_timer = new Timer(TimerRun, null, 10, 10); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 析构函数 |
||||||
|
/// </summary> |
||||||
|
~TriggerQueue() |
||||||
|
{ |
||||||
|
Dispose(false); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 出队列处理。 |
||||||
|
/// </summary> |
||||||
|
public Action<T> OnDequeue { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 发生错误 |
||||||
|
/// </summary> |
||||||
|
public Action<Exception> OnError { get; set; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 是否处于发送状态 |
||||||
|
/// </summary> |
||||||
|
public bool Sending |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
using (new ReadLock(m_lockSlim)) |
||||||
|
{ |
||||||
|
return m_sending; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private set |
||||||
|
{ |
||||||
|
using (new WriteLock(m_lockSlim)) |
||||||
|
{ |
||||||
|
m_sending = value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 发送 |
||||||
|
/// </summary> |
||||||
|
public void Enqueue(T data) |
||||||
|
{ |
||||||
|
m_queue.Enqueue(data); |
||||||
|
if (SwitchToRun()) |
||||||
|
{ |
||||||
|
ThreadPool.QueueUserWorkItem(BeginTrigger); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 释放 |
||||||
|
/// </summary> |
||||||
|
/// <param name="disposing"></param> |
||||||
|
protected override void Dispose(bool disposing) |
||||||
|
{ |
||||||
|
m_timer.SafeDispose(); |
||||||
|
m_queue.Clear(); |
||||||
|
base.Dispose(disposing); |
||||||
|
} |
||||||
|
|
||||||
|
private void BeginTrigger(object o) |
||||||
|
{ |
||||||
|
while (true) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
if (m_queue.TryDequeue(out T data)) |
||||||
|
{ |
||||||
|
OnDequeue?.Invoke(data); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
OnError?.Invoke(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
Sending = false; |
||||||
|
} |
||||||
|
|
||||||
|
private bool SwitchToRun() |
||||||
|
{ |
||||||
|
using (new ReadLock(m_lockSlim)) |
||||||
|
{ |
||||||
|
if (m_sending) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
m_sending = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void TimerRun(object state) |
||||||
|
{ |
||||||
|
if (SwitchToRun()) |
||||||
|
{ |
||||||
|
BeginTrigger(null); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 4dfc17adf79b3884999bab5657ac134b |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Collections.Specialized; |
||||||
|
using System.Diagnostics; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// IgnoreCaseNameValueCollection |
||||||
|
/// </summary> |
||||||
|
[DebuggerTypeProxy(typeof(NameValueCollectionDebugView))] |
||||||
|
public class IgnoreCaseNameValueCollection : NameValueCollection |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// IgnoreCaseNameValueCollection |
||||||
|
/// </summary> |
||||||
|
public IgnoreCaseNameValueCollection() : base(StringComparer.OrdinalIgnoreCase) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 5522036c49582034590d003b163b851f |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.Specialized; |
||||||
|
using System.Diagnostics; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// NameValueCollectionDebugView |
||||||
|
/// </summary> |
||||||
|
public class NameValueCollectionDebugView |
||||||
|
{ |
||||||
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] |
||||||
|
private readonly NameValueCollection m_nameValue; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// NameValueCollectionDebugView |
||||||
|
/// </summary> |
||||||
|
/// <param name="nameValue"></param> |
||||||
|
public NameValueCollectionDebugView(NameValueCollection nameValue) |
||||||
|
{ |
||||||
|
m_nameValue = nameValue; |
||||||
|
} |
||||||
|
|
||||||
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] |
||||||
|
private Dictionary<string, string> KV |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
var dic = new Dictionary<string, string>(); |
||||||
|
foreach (var item in m_nameValue.AllKeys) |
||||||
|
{ |
||||||
|
dic.TryAdd(item, m_nameValue[item]); |
||||||
|
} |
||||||
|
return dic; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 88bfd0b84b3ab6541bf110ed39ab750c |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 04e4682b3f3e98944bb15bf63019deb0 |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,137 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 运行配置类 |
||||||
|
/// </summary> |
||||||
|
public abstract class AppConfigBase |
||||||
|
{ |
||||||
|
private readonly string m_fullPath; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="fullPath"></param> |
||||||
|
public AppConfigBase(string fullPath) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(fullPath)) |
||||||
|
{ |
||||||
|
throw new ArgumentException($"“{nameof(fullPath)}”不能为 null 或空。", nameof(fullPath)); |
||||||
|
} |
||||||
|
|
||||||
|
m_fullPath = fullPath; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 保存配置 |
||||||
|
/// </summary> |
||||||
|
/// <param name="overwrite"></param> |
||||||
|
/// <param name="msg"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool Save(bool overwrite, out string msg) |
||||||
|
{ |
||||||
|
if (overwrite == false && File.Exists(m_fullPath)) |
||||||
|
{ |
||||||
|
msg = null; |
||||||
|
return true; |
||||||
|
} |
||||||
|
try |
||||||
|
{ |
||||||
|
File.WriteAllText(m_fullPath, this.ToJson()); |
||||||
|
msg = null; |
||||||
|
return true; |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
msg = ex.Message; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 加载配置 |
||||||
|
/// </summary> |
||||||
|
/// <param name="msg"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public bool Load(out string msg) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
if (!File.Exists(m_fullPath)) |
||||||
|
{ |
||||||
|
Save(false, out _); |
||||||
|
} |
||||||
|
var obj = File.ReadAllText(m_fullPath).FromJson(GetType()); |
||||||
|
var ps = GetType().GetProperties(); |
||||||
|
|
||||||
|
foreach (var item in ps) |
||||||
|
{ |
||||||
|
item.SetValue(this, item.GetValue(obj)); |
||||||
|
} |
||||||
|
msg = null; |
||||||
|
return true; |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
msg = ex.Message; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取默认配置。 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="T"></typeparam> |
||||||
|
/// <returns></returns> |
||||||
|
public static T GetDefault<T>() where T : AppConfigBase, new() |
||||||
|
{ |
||||||
|
Type type = typeof(T); |
||||||
|
if (list.TryGetValue(type, out object value)) |
||||||
|
{ |
||||||
|
return (T)value; |
||||||
|
} |
||||||
|
T _default = ((T)Activator.CreateInstance(typeof(T))); |
||||||
|
_default.Load(out _); |
||||||
|
list.Add(type, _default); |
||||||
|
return _default; |
||||||
|
} |
||||||
|
|
||||||
|
private static readonly Dictionary<Type, object> list = new Dictionary<Type, object>(); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取默认配置,每次调用该方法时,都会重新加载配置。 |
||||||
|
/// </summary> |
||||||
|
/// <typeparam name="T"></typeparam> |
||||||
|
/// <returns></returns> |
||||||
|
public static T GetNewDefault<T>() where T : AppConfigBase, new() |
||||||
|
{ |
||||||
|
T _default = ((T)Activator.CreateInstance(typeof(T))); |
||||||
|
_default.Load(out _); |
||||||
|
if (list.ContainsKey(_default.GetType())) |
||||||
|
{ |
||||||
|
list[_default.GetType()] = _default; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
list.Add(_default.GetType(), _default); |
||||||
|
} |
||||||
|
return _default; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 8e30506e32e22734fad05dcb75b92623 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// DateExtensions |
||||||
|
/// </summary> |
||||||
|
public static class DateExtensions |
||||||
|
{ |
||||||
|
private static readonly DateTime m_utc_time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 将时间转为毫秒级别的短整形 |
||||||
|
/// </summary> |
||||||
|
/// <param name="time"></param> |
||||||
|
/// <returns></returns> |
||||||
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||||
|
public static uint ConvertTime(this in DateTime time) |
||||||
|
{ |
||||||
|
return (uint)(Convert.ToInt64(time.Subtract(m_utc_time).TotalMilliseconds) & 0xffffffff); |
||||||
|
} |
||||||
|
|
||||||
|
private static readonly DateTimeOffset m_utc1970 = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 将时间转为毫秒级别的短整形 |
||||||
|
/// </summary> |
||||||
|
/// <param name="time"></param> |
||||||
|
/// <returns></returns> |
||||||
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||||
|
public static uint ConvertTime(this in DateTimeOffset time) |
||||||
|
{ |
||||||
|
return (uint)(Convert.ToInt64(time.Subtract(m_utc1970).TotalMilliseconds) & 0xffffffff); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 1364c97b7a69efa47ba79c61b5e374d6 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 具有释放的对象。 |
||||||
|
/// 并未实现析构函数相关。 |
||||||
|
/// </summary> |
||||||
|
public class DisposableObject : IDisposable |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 判断是否已释放。 |
||||||
|
/// </summary> |
||||||
|
private volatile bool m_disposedValue; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 标识该对象是否已被释放 |
||||||
|
/// </summary> |
||||||
|
public bool DisposedValue { get => m_disposedValue; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 调用释放,切换释放状态。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="disposing"></param> |
||||||
|
protected virtual void Dispose(bool disposing) |
||||||
|
{ |
||||||
|
m_disposedValue = true; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 释放资源 |
||||||
|
/// </summary> |
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
Dispose(disposing: true); |
||||||
|
GC.SuppressFinalize(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: d5952585b3932c243a25c82b10419b9a |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 57205d24b8d6dfd498e25550f261ed3f |
||||||
|
folderAsset: yes |
||||||
|
DefaultImporter: |
||||||
|
externalObjects: {} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 大小端类型 |
||||||
|
/// </summary> |
||||||
|
public enum EndianType |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 小端模式 |
||||||
|
/// </summary> |
||||||
|
Little, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 大端模式 |
||||||
|
/// </summary> |
||||||
|
Big |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 02030991d05c18c4ab0e21a290d78451 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 结果类型 |
||||||
|
/// </summary> |
||||||
|
public enum ResultCode |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 默认 |
||||||
|
/// </summary> |
||||||
|
Default, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 错误 |
||||||
|
/// </summary> |
||||||
|
Error, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 异常 |
||||||
|
/// </summary> |
||||||
|
Exception, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 成功 |
||||||
|
/// </summary> |
||||||
|
Success, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 失败 |
||||||
|
/// </summary> |
||||||
|
Fail, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作超时 |
||||||
|
/// </summary> |
||||||
|
Overtime, |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 操作取消 |
||||||
|
/// </summary> |
||||||
|
Canceled |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 9c0015ad37647324088a14cef9bc3160 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,112 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 流量控制 |
||||||
|
/// </summary> |
||||||
|
public class FlowGate |
||||||
|
{ |
||||||
|
private readonly Stopwatch m_stopwatch; |
||||||
|
|
||||||
|
private long m_timeTick; |
||||||
|
|
||||||
|
private long m_transferLength; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
public FlowGate() |
||||||
|
{ |
||||||
|
m_stopwatch = new Stopwatch(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 最大值 |
||||||
|
/// </summary> |
||||||
|
public long Maximum { get; set; } = long.MaxValue; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 最长休眠周期。默认为5*1000ms. |
||||||
|
/// <para>当设置为5000时,假如设置的<see cref="Maximum"/>=10,而一次递增了100,则理应会休眠10s,但是会休眠5s。反之,如果设置1,则每秒周期都会清空。</para> |
||||||
|
/// </summary> |
||||||
|
public int MaximumPeriod { get; set; } = 5000; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 检测等待 |
||||||
|
/// </summary> |
||||||
|
public void AddCheckWait(int increment) |
||||||
|
{ |
||||||
|
if (GetNowTick() - m_timeTick > 0) |
||||||
|
{ |
||||||
|
//时间过了一秒 |
||||||
|
m_timeTick = FlowGate.GetNowTick(); |
||||||
|
m_transferLength = 0; |
||||||
|
m_stopwatch.Restart(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
//在这一秒中 |
||||||
|
if (Interlocked.Add(ref m_transferLength, increment) > Maximum) |
||||||
|
{ |
||||||
|
//上传饱和 |
||||||
|
m_stopwatch.Stop(); |
||||||
|
int sleepTime = 1000 - (int)m_stopwatch.ElapsedMilliseconds <= 0 ? 0 : GetBaseNum() - (int)m_stopwatch.ElapsedMilliseconds; |
||||||
|
Thread.Sleep(sleepTime); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 检测等待 |
||||||
|
/// </summary> |
||||||
|
/// <param name="increment"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task AddCheckWaitAsync(int increment) |
||||||
|
{ |
||||||
|
if (GetNowTick() - m_timeTick > 0) |
||||||
|
{ |
||||||
|
//时间过了一秒 |
||||||
|
m_timeTick = FlowGate.GetNowTick(); |
||||||
|
m_transferLength = 0; |
||||||
|
m_stopwatch.Restart(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
//在这一秒中 |
||||||
|
if (Interlocked.Add(ref m_transferLength, increment) > Maximum) |
||||||
|
{ |
||||||
|
//上传饱和 |
||||||
|
m_stopwatch.Stop(); |
||||||
|
int sleepTime = 1000 - (int)m_stopwatch.ElapsedMilliseconds <= 0 ? 0 : GetBaseNum() - (int)m_stopwatch.ElapsedMilliseconds; |
||||||
|
await Task.Delay(sleepTime); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static long GetNowTick() |
||||||
|
{ |
||||||
|
return DateTime.Now.Ticks / 10000000; |
||||||
|
} |
||||||
|
|
||||||
|
private int GetBaseNum() |
||||||
|
{ |
||||||
|
return Math.Min((int)((double)m_transferLength / Maximum * 1000), MaximumPeriod); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 8c1a6d5576d49544495a8f439e903e2d |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 16618c64fd240214e9260bbfbd8b5771 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 返回通知接口 |
||||||
|
/// </summary> |
||||||
|
public interface IResult |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 是否成功 |
||||||
|
/// </summary> |
||||||
|
ResultCode ResultCode { get; } |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 消息 |
||||||
|
/// </summary> |
||||||
|
string Message { get; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 3bdf48651147c0845b6ed8fcc01e04a2 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 规范写端口,提供更多扩展 |
||||||
|
/// </summary> |
||||||
|
public interface IWrite |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 写入 |
||||||
|
/// </summary> |
||||||
|
/// <param name="buffer"></param> |
||||||
|
/// <param name="offset"></param> |
||||||
|
/// <param name="length"></param> |
||||||
|
void Write(byte[] buffer, int offset, int length); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 写入 |
||||||
|
/// </summary> |
||||||
|
/// <param name="buffer"></param> |
||||||
|
void Write(byte[] buffer); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: b77435cd562c9f3459143f489d6de09d |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 读取锁 |
||||||
|
/// </summary> |
||||||
|
public struct ReadLock : IDisposable |
||||||
|
{ |
||||||
|
private readonly ReaderWriterLockSlim m_locks; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="locks"></param> |
||||||
|
public ReadLock(ReaderWriterLockSlim locks) |
||||||
|
{ |
||||||
|
m_locks = locks; |
||||||
|
m_locks.EnterReadLock(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 释放 |
||||||
|
/// </summary> |
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
m_locks.ExitReadLock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 写入锁 |
||||||
|
/// </summary> |
||||||
|
public struct WriteLock : IDisposable |
||||||
|
{ |
||||||
|
private readonly ReaderWriterLockSlim m_locks; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 构造函数 |
||||||
|
/// </summary> |
||||||
|
/// <param name="locks"></param> |
||||||
|
public WriteLock(ReaderWriterLockSlim locks) |
||||||
|
{ |
||||||
|
m_locks = locks; |
||||||
|
m_locks.EnterWriteLock(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 释放 |
||||||
|
/// </summary> |
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
m_locks.ExitWriteLock(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: ecccffc11f9e15e42b94c5b36250ac83 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
using System.Collections.Specialized; |
||||||
|
|
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 元数据键值对。 |
||||||
|
/// </summary> |
||||||
|
[FastConverter(typeof(MetadataFastBinaryConverter))] |
||||||
|
public class Metadata : NameValueCollection, IPackage |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 元数据键值对。 |
||||||
|
/// </summary> |
||||||
|
public Metadata() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 添加。如果键存在,将被覆盖。 |
||||||
|
/// </summary> |
||||||
|
/// <param name="name"></param> |
||||||
|
/// <param name="value"></param> |
||||||
|
public new Metadata Add(string name, string value) |
||||||
|
{ |
||||||
|
base.Add(name, value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 打包 |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteBlock"></param> |
||||||
|
public void Package(ByteBlock byteBlock) |
||||||
|
{ |
||||||
|
byteBlock.Write(Count); |
||||||
|
foreach (var item in AllKeys) |
||||||
|
{ |
||||||
|
byteBlock.Write(item); |
||||||
|
byteBlock.Write(this[item]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 解包 |
||||||
|
/// </summary> |
||||||
|
/// <param name="byteBlock"></param> |
||||||
|
public void Unpackage(ByteBlock byteBlock) |
||||||
|
{ |
||||||
|
int count = byteBlock.ReadInt32(); |
||||||
|
for (int i = 0; i < count; i++) |
||||||
|
{ |
||||||
|
string key = byteBlock.ReadString(); |
||||||
|
string value = byteBlock.ReadString(); |
||||||
|
Add(key, value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: a579508f5dee17049b2332cd9d7dc4da |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
//------------------------------------------------------------------------------ |
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
||||||
|
// Github源代码仓库:https://github.com/RRQM |
||||||
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
||||||
|
// 交流QQ群:234762506 |
||||||
|
// 感谢您的下载和使用 |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
//------------------------------------------------------------------------------ |
||||||
|
namespace TouchSocket.Core |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// MetadataFastBinaryConverter |
||||||
|
/// </summary> |
||||||
|
internal sealed class MetadataFastBinaryConverter : FastBinaryConverter<Metadata> |
||||||
|
{ |
||||||
|
protected override Metadata Read(byte[] buffer, int offset, int len) |
||||||
|
{ |
||||||
|
ByteBlock byteBlock = new ByteBlock(buffer); |
||||||
|
byteBlock.Pos = offset; |
||||||
|
Metadata metadata = new Metadata(); |
||||||
|
while (byteBlock.Pos < offset + len) |
||||||
|
{ |
||||||
|
metadata.Add(byteBlock.ReadString(), byteBlock.ReadString()); |
||||||
|
} |
||||||
|
return metadata; |
||||||
|
} |
||||||
|
|
||||||
|
protected override int Write(ByteBlock byteBlock, Metadata obj) |
||||||
|
{ |
||||||
|
int pos = byteBlock.Pos; |
||||||
|
foreach (var item in obj.AllKeys) |
||||||
|
{ |
||||||
|
byteBlock.Write(item); |
||||||
|
byteBlock.Write(obj[item]); |
||||||
|
} |
||||||
|
return byteBlock.Pos - pos; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 59374d752e857c343a1a0e363ba18228 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 72cd5f79ed1863a4884d43729bf5be92 |
||||||
|
MonoImporter: |
||||||
|
externalObjects: {} |
||||||
|
serializedVersion: 2 |
||||||
|
defaultReferences: [] |
||||||
|
executionOrder: 0 |
||||||
|
icon: {instanceID: 0} |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue