commit
a2715844f6
1303 changed files with 85594 additions and 0 deletions
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: bbd9404d74d63f642829c25989306635 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: bdb07b9e9393c8040b9000470b1b9119 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
|
After Width: | Height: | Size: 694 KiB |
@ -0,0 +1,135 @@ |
||||
fileFormatVersion: 2 |
||||
guid: f1ba7622665bf7e44aee3f43fc62e51d |
||||
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: d28ff85c01985904bb98e5bc03251ff7 |
||||
NativeFormatImporter: |
||||
externalObjects: {} |
||||
mainObjectFileID: 0 |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
Binary file not shown.
@ -0,0 +1,106 @@ |
||||
fileFormatVersion: 2 |
||||
guid: bf4ff8034d8337a4d8f291f88a544688 |
||||
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: ed1b2724730cc594aa8c79b70d2deb24 |
||||
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: 9183cab5601042546807088e330d8679 |
||||
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: 0305e3a35dffee44ea72d74b0d43fada |
||||
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: a6f2d24fab7bde647aee587d1edea618 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
Binary file not shown.
@ -0,0 +1,33 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 33de35e2f8943a64da79f058f219b36d |
||||
PluginImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
iconMap: {} |
||||
executionOrder: {} |
||||
defineConstraints: [] |
||||
isPreloaded: 0 |
||||
isOverridable: 0 |
||||
isExplicitlyReferenced: 0 |
||||
validateReferences: 1 |
||||
platformData: |
||||
- first: |
||||
Any: |
||||
second: |
||||
enabled: 1 |
||||
settings: {} |
||||
- first: |
||||
Editor: Editor |
||||
second: |
||||
enabled: 0 |
||||
settings: |
||||
DefaultValueInitialized: true |
||||
- first: |
||||
Windows Store Apps: WindowsStoreApps |
||||
second: |
||||
enabled: 0 |
||||
settings: |
||||
CPU: AnyCPU |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 74936e8bf9a4b144f91a9ae99cb9b6a5 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
||||
fileFormatVersion: 2 |
||||
guid: c9b621f62f7be4c42a90d1971e952711 |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: ca9f7a4701b0fa14d88c58c3b79ab9de |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,698 @@ |
||||
using NaughtyAttributes; |
||||
using System.Collections.Generic; |
||||
using TouchSocket.Core; |
||||
using UnityEngine; |
||||
|
||||
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; |
||||
|
||||
[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("[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) |
||||
{ |
||||
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; |
||||
} |
||||
} |
||||
|
||||
//z轴朝上的右手坐标系 四元数 转换为 Y轴朝上的左手坐标系 四元数 |
||||
private Quaternion ConvertQuaternion(Quaternion quat) |
||||
{ |
||||
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); |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
fileFormatVersion: 2 |
||||
guid: ff3bfb33faef00d40880dd5952fd19e9 |
||||
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: 704298a683566b5449b0b21443685ded |
||||
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: d694a138ada737547be655b517ff143b |
||||
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: 1fe0a761cd51ae048b2fd88ac356edf4 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: f94ed6072f4ff6646becdf1839ffe326 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: c37048798d5eed44db29842d399ae29d |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: b7db8e71011decc4780329933fb537a7 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: e1e6048f206b62443a41e15c5c03aef1 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 54ff10f3a2b47fe439f12a9a7f84c22d |
||||
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: e198fd391e85af146959f92d37ba5894 |
||||
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: fb91b6d0d3153b2418cbb08b593d71db |
||||
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: 19414046bb9249f4b9d555defe71fe9e |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 23f86139776b6194e819e048e47eb60b |
||||
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: 663cd719b9c7a4241a188a52bfb5dc8d |
||||
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: 9680faf7cadf2594abefdf5ab80d0388 |
||||
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: 3054dead237b63e428a357750722f254 |
||||
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: ecbef6cbd2aa5e84e946e3dbe8516d28 |
||||
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: 7dfeb47bd7624e747958892b91e6a6e3 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: c4a241f9e49967944989e3b7c9ee4cf2 |
||||
folderAsset: yes |
||||
DefaultImporter: |
||||
externalObjects: {} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: d5f7da4939a12db4a91dc35545db7a9b |
||||
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: 2c3d244f996ca2446ab0fec67556bd84 |
||||
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: 673586cb35b70a046985566deb8b8fef |
||||
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: 96aafe28e2306da4bb93c80f1e2b5adb |
||||
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: 8e7021efeb05d1a44b90744bce5ce182 |
||||
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: 0247c53477b0b394ca5c24ed020ba16f |
||||
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: aacbd1d9a5a0feb45bae13256a6a906b |
||||
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: c63f43c6a4d54e5499011ef334467ad1 |
||||
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: 987fdf4e6e637724ea77a76634e1a790 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: fb889b7d313e6284b9f776c89ca01fce |
||||
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: 6ed702ed827ce43409e18eb8fc27e7e8 |
||||
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: 48540296bedd2b9429e3e9c67a01ea7c |
||||
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: f554ce7c1a71cf7428880cc87acd8929 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,8 @@ |
||||
fileFormatVersion: 2 |
||||
guid: b7889a97bcee5e84bbebb6c2b76514b5 |
||||
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: 10c00a81122ee2449bb07b625a8fb1ce |
||||
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: 5c6cd2fbac739ef478c9731e4e3d7454 |
||||
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: 5ff12a1b57c52874d9827f83bc01350e |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,11 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 7fbf96b0cdf447d42ac9789d27263955 |
||||
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: c6dda961a9316114e8c8d073c5e32267 |
||||
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: 0fcb41a96551b7047aab8e24c5cd7bf9 |
||||
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: 32062fa43b30c4748bb562c907c8074d |
||||
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: 6f12db4dc096b0148b954a76cb5a8453 |
||||
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: 09e52d077aabd034b87ae6c6aeabc08e |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,11 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 1b8cb16ca24672f4f8e0089982269460 |
||||
MonoImporter: |
||||
externalObjects: {} |
||||
serializedVersion: 2 |
||||
defaultReferences: [] |
||||
executionOrder: 0 |
||||
icon: {instanceID: 0} |
||||
userData: |
||||
assetBundleName: |
||||
assetBundleVariant: |
||||
@ -0,0 +1,268 @@ |
||||
//------------------------------------------------------------------------------ |
||||
// 此代码版权(除特别声明或在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 TouchSocket.Resources; |
||||
|
||||
namespace TouchSocket.Core |
||||
{ |
||||
/// <summary> |
||||
/// 结果返回 |
||||
/// </summary> |
||||
public struct Result : IResult |
||||
{ |
||||
/// <summary> |
||||
/// 成功 |
||||
/// </summary> |
||||
public static readonly Result Success = new Result(ResultCode.Success, "Success"); |
||||
|
||||
/// <summary> |
||||
/// 初始状态 |
||||
/// </summary> |
||||
public static readonly Result Default = new Result(ResultCode.Default, "Default"); |
||||
|
||||
/// <summary> |
||||
/// 未知失败 |
||||
/// </summary> |
||||
public static readonly Result UnknownFail = new Result(ResultCode.Fail, TouchSocketStatus.UnknownError.GetDescription()); |
||||
|
||||
/// <summary> |
||||
/// 超时 |
||||
/// </summary> |
||||
public static readonly Result Overtime = new Result(ResultCode.Overtime, TouchSocketStatus.Overtime.GetDescription()); |
||||
|
||||
/// <summary> |
||||
/// 取消 |
||||
/// </summary> |
||||
public static readonly Result Canceled = new Result(ResultCode.Canceled, TouchSocketStatus.Canceled.GetDescription()); |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="resultCode"></param> |
||||
/// <param name="message"></param> |
||||
public Result(ResultCode resultCode, string message) |
||||
{ |
||||
ResultCode = resultCode; |
||||
Message = message; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="result"></param> |
||||
public Result(IResult result) |
||||
{ |
||||
ResultCode = result.ResultCode; |
||||
Message = result.Message; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="exception"></param> |
||||
public Result(Exception exception) |
||||
{ |
||||
ResultCode = ResultCode.Exception; |
||||
Message = exception.Message; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="resultCode"></param> |
||||
public Result(ResultCode resultCode) |
||||
{ |
||||
ResultCode = resultCode; |
||||
Message = resultCode.GetDescription(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// <inheritdoc/> |
||||
/// </summary> |
||||
public ResultCode ResultCode { get; private set; } |
||||
|
||||
/// <summary> |
||||
/// <inheritdoc/> |
||||
/// </summary> |
||||
public string Message { get; private set; } |
||||
|
||||
/// <summary> |
||||
/// 创建来自<see cref="ResultCode.Canceled"/>的<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="msg"></param> |
||||
/// <returns></returns> |
||||
public static Result FromCanceled(string msg) |
||||
{ |
||||
return new Result(ResultCode.Canceled, msg); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 创建来自<see cref="ResultCode.Error"/>的<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="msg"></param> |
||||
/// <returns></returns> |
||||
public static Result FromError(string msg) |
||||
{ |
||||
return new Result(ResultCode.Error, msg); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 创建来自<see cref="ResultCode.Exception"/>的<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="msg"></param> |
||||
/// <returns></returns> |
||||
public static Result FromException(string msg) |
||||
{ |
||||
return new Result(ResultCode.Exception, msg); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 创建来自<see cref="ResultCode.Overtime"/>的<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="msg"></param> |
||||
/// <returns></returns> |
||||
public static Result FromFail(string msg) |
||||
{ |
||||
return new Result(ResultCode.Fail, msg); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 创建来自<see cref="ResultCode.Overtime"/>的<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="msg"></param> |
||||
/// <returns></returns> |
||||
public static Result FromOvertime(string msg) |
||||
{ |
||||
return new Result(ResultCode.Overtime, msg); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 创建来自<see cref="ResultCode.Success"/>的<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="msg"></param> |
||||
/// <returns></returns> |
||||
public static Result FromSuccess(string msg) |
||||
{ |
||||
return new Result(ResultCode.Success, msg); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// ToString |
||||
/// </summary> |
||||
/// <returns></returns> |
||||
public override string ToString() |
||||
{ |
||||
return $"类型:{ResultCode},信息:{Message}"; |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 结果返回 |
||||
/// </summary> |
||||
public class ResultBase : IResult |
||||
{ |
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="resultCode"></param> |
||||
/// <param name="message"></param> |
||||
public ResultBase(ResultCode resultCode, string message) |
||||
{ |
||||
ResultCode = resultCode; |
||||
Message = message; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="resultCode"></param> |
||||
public ResultBase(ResultCode resultCode) |
||||
{ |
||||
ResultCode = resultCode; |
||||
Message = resultCode.GetDescription(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="result"></param> |
||||
public ResultBase(Result result) |
||||
{ |
||||
ResultCode = result.ResultCode; |
||||
Message = result.Message; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
public ResultBase() |
||||
{ |
||||
} |
||||
|
||||
/// <summary> |
||||
/// <inheritdoc/> |
||||
/// </summary> |
||||
public ResultCode ResultCode { get; protected set; } |
||||
|
||||
/// <summary> |
||||
/// <inheritdoc/> |
||||
/// </summary> |
||||
public string Message { get; protected set; } |
||||
|
||||
/// <summary> |
||||
/// ToString |
||||
/// </summary> |
||||
/// <returns></returns> |
||||
public override string ToString() |
||||
{ |
||||
return $"类型:{ResultCode},信息:{Message}"; |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// ResultExtensions |
||||
/// </summary> |
||||
public static class ResultExtensions |
||||
{ |
||||
/// <summary> |
||||
/// 是否成功。 |
||||
/// </summary> |
||||
/// <param name="result"></param> |
||||
/// <returns></returns> |
||||
public static bool IsSuccess(this IResult result) |
||||
{ |
||||
return result.ResultCode == ResultCode.Success; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 是否没有成功。 |
||||
/// </summary> |
||||
/// <param name="result"></param> |
||||
/// <returns></returns> |
||||
public static bool NotSuccess(this IResult result) |
||||
{ |
||||
return result.ResultCode != ResultCode.Success; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 转换为<see cref="Result"/> |
||||
/// </summary> |
||||
/// <param name="result"></param> |
||||
/// <returns></returns> |
||||
public static Result ToResult(this IResult result) |
||||
{ |
||||
return new Result(result); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
fileFormatVersion: 2 |
||||
guid: 742b24f62b34d924295db0b03167f36b |
||||
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