diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml new file mode 100644 index 0000000..f93244c --- /dev/null +++ b/.github/workflows/nuget-publish.yml @@ -0,0 +1,71 @@ +name: NuGet Publish + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + name: Build & Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore + run: dotnet restore src/NativeWebSocket/NativeWebSocket.csproj + + - name: Build + run: dotnet build src/NativeWebSocket/NativeWebSocket.csproj -c Release --no-restore + + publish: + name: Publish to NuGet + runs-on: ubuntu-latest + needs: [build] + # TODO: revert to: if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Check for version change + id: version + run: | + CURRENT=$(grep '' src/NativeWebSocket/NativeWebSocket.csproj | sed 's/.*\(.*\)<\/Version>.*/\1/') + PREVIOUS=$(git show HEAD~1:src/NativeWebSocket/NativeWebSocket.csproj 2>/dev/null | grep '' | sed 's/.*\(.*\)<\/Version>.*/\1/' || echo "") + echo "current=$CURRENT" >> $GITHUB_OUTPUT + echo "previous=$PREVIOUS" >> $GITHUB_OUTPUT + if [ "$CURRENT" != "$PREVIOUS" ]; then + echo "changed=true" >> $GITHUB_OUTPUT + echo "Version changed: $PREVIOUS -> $CURRENT" + else + echo "changed=false" >> $GITHUB_OUTPUT + echo "Version unchanged: $CURRENT" + fi + + - name: Pack NativeWebSocket + # TODO: revert to: if: steps.version.outputs.changed == 'true' + run: dotnet pack src/NativeWebSocket/NativeWebSocket.csproj -c Release -o packages/ + + - name: Pack NativeWebSocket.MonoGame + # TODO: revert to: if: steps.version.outputs.changed == 'true' + run: dotnet pack integrations/MonoGame/NativeWebSocket.MonoGame.csproj -c Release -o packages/ + + - name: Publish to NuGet + # TODO: revert to: if: steps.version.outputs.changed == 'true' + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + run: dotnet nuget push packages/*.nupkg --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.github/workflows/unity-upm-release.yml b/.github/workflows/unity-upm-release.yml new file mode 100644 index 0000000..3db3217 --- /dev/null +++ b/.github/workflows/unity-upm-release.yml @@ -0,0 +1,232 @@ +name: Unity UPM Release + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + checkSemver: + name: Check for Semver Change + runs-on: ubuntu-latest + outputs: + semver-updated: ${{ steps.check.outputs.changed }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - uses: EndBug/version-check@v1 + id: check + with: + file-name: integrations/Unity/package.json + diff-search: true + token: ${{ secrets.GITHUB_TOKEN }} + - name: Log when changed + if: steps.check.outputs.changed == 'true' + run: 'echo "Version change found in commit ${{ steps.check.outputs.commit }}! New version: ${{ steps.check.outputs.version }} (${{ steps.check.outputs.type }})"' + + updateUPM: + name: Update UPM branch + runs-on: ubuntu-latest + needs: [checkSemver] + if: needs.checkSemver.outputs.semver-updated == 'true' + steps: + - uses: actions/checkout@v4 + + - name: Assemble UPM package + run: | + mkdir -p upm-package/WebSocket + + # Core library files (single source of truth) + cp src/NativeWebSocket/IWebSocket.cs upm-package/WebSocket/ + cp src/NativeWebSocket/WebSocketTypes.cs upm-package/WebSocket/ + + # Wrap core WebSocket.cs with Unity conditional compilation guard + { echo '#if !UNITY_WEBGL || UNITY_EDITOR'; echo ''; cat src/NativeWebSocket/WebSocket.cs; echo ''; echo '#endif'; } > upm-package/WebSocket/WebSocket.cs + + # Unity-specific files (WebGL platform support) + cp integrations/Unity/WebSocketWebGL.cs upm-package/WebSocket/ + cp integrations/Unity/WebSocketFactoryWebGL.cs upm-package/WebSocket/ + cp integrations/Unity/WebSocket.jslib upm-package/WebSocket/ + cp integrations/Unity/WebSocket.jspre upm-package/WebSocket/ + cp integrations/Unity/endel.nativewebsocket.asmdef upm-package/WebSocket/ + + # Package metadata and samples + cp integrations/Unity/package.json upm-package/ + cp -r integrations/Unity/Samples~ upm-package/ + + - name: Push package directory to subtree + uses: s0/git-publish-subdir-action@develop + env: + REPO: self + BRANCH: upm-2.0 + FOLDER: upm-package/ + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + createPackage: + name: Create Unity Package and Release + runs-on: ubuntu-latest + needs: [checkSemver] + if: needs.checkSemver.outputs.semver-updated == 'true' + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Assemble package directory + run: | + mkdir -p upm-package/Assets/WebSocket + + # Core library files + cp src/NativeWebSocket/IWebSocket.cs upm-package/Assets/WebSocket/ + cp src/NativeWebSocket/WebSocketTypes.cs upm-package/Assets/WebSocket/ + + # Wrap core WebSocket.cs with Unity conditional compilation guard + { echo '#if !UNITY_WEBGL || UNITY_EDITOR'; echo ''; cat src/NativeWebSocket/WebSocket.cs; echo ''; echo '#endif'; } > upm-package/Assets/WebSocket/WebSocket.cs + + # Unity-specific files + cp integrations/Unity/WebSocketWebGL.cs upm-package/Assets/WebSocket/ + cp integrations/Unity/WebSocketFactoryWebGL.cs upm-package/Assets/WebSocket/ + cp integrations/Unity/WebSocket.jslib upm-package/Assets/WebSocket/ + cp integrations/Unity/WebSocket.jspre upm-package/Assets/WebSocket/ + cp integrations/Unity/endel.nativewebsocket.asmdef upm-package/Assets/WebSocket/ + + # Package metadata + cp integrations/Unity/package.json upm-package/Assets/ + + - name: Generate .meta files + run: | + generate_meta() { + local file="$1" + local guid + guid=$(echo -n "$file" | md5sum | cut -c1-32) + local ext="${file##*.}" + local meta="${file}.meta" + + case "$ext" in + cs) + cat > "$meta" < "$meta" < "$meta" < upm-package/Assets/WebSocket.meta < ../metaList + cat ../metaList + + - name: Extract Version + id: version + uses: notiz-dev/github-action-json-property@release + with: + path: 'integrations/Unity/package.json' + prop_path: 'version' + - run: echo ${{steps.version.outputs.prop}} + + - name: Generate Unity Package + id: build-package + uses: pCYSl5EDgo/create-unitypackage@master + with: + package-path: 'NativeWebSocket.unitypackage' + include-files: metaList + project-folder: upm-package + + - name: Upload Package + uses: actions/upload-artifact@v4 + with: + path: ./NativeWebSocket.unitypackage + name: NativeWebSocket + + - name: Changelog + uses: scottbrenner/generate-changelog-action@master + id: Changelog + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.version.outputs.prop }} + release_name: ${{ steps.version.outputs.prop }} + body: | + ${{ steps.Changelog.outputs.changelog }} + draft: false + prerelease: true + + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./NativeWebSocket.unitypackage + asset_name: NativeWebSocket.unitypackage + asset_content_type: application/x-gzip diff --git a/.github/workflows/upmsemver.yml b/.github/workflows/upmsemver.yml deleted file mode 100644 index 57b149e..0000000 --- a/.github/workflows/upmsemver.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Update Unity UPM semantic versioning - -on: - push: - branches: - - master - -jobs: - checkSemver: - name: Check for Semver Change - runs-on: ubuntu-latest - outputs: - semver-updated: ${{ steps.check.outputs.changed }} - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - uses: EndBug/version-check@v1 - id: check - with: - file-name: NativeWebSocket/Assets/package.json - diff-search: true - token: ${{ secrets.GITHUB_TOKEN }} - - name: Log when changed - if: steps.check.outputs.changed == 'true' - run: 'echo "Version change found in commit ${{ steps.check.outputs.commit }}! New version: ${{ steps.check.outputs.version }} (${{ steps.check.outputs.type }})"' - updateUPM: - name: Update UPM branch - runs-on: ubuntu-latest - needs: [checkSemver] - if: needs.checkSemver.outputs.semver-updated == 'true' - steps: - # Copy correct directory into upm branch - - uses: actions/checkout@v2 - - - name: Push package directory to subtree - uses: s0/git-publish-subdir-action@develop - env: - REPO: self - BRANCH: upm - FOLDER: NativeWebSocket/Assets/ - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - createPackage: - name: Create Unity Package and Release - runs-on: ubuntu-latest - needs: [checkSemver] - if: needs.checkSemver.outputs.semver-updated == 'true' - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - name: Gather files - run: | - echo "NativeWebSocket/Assets/WebSocket.meta" > metaList - find NativeWebSocket/Assets/WebSocket/ -name \*.meta >> metaList - - name: Extract Version - id: version - uses: notiz-dev/github-action-json-property@release - with: - path: 'NativeWebSocket/Assets/package.json' - prop_path: 'version' - - run: echo ${{steps.version.outputs.prop}} - - name: Create Directory - run: mkdir Release - - name: Generate Unity Package - id: build-package - uses: pCYSl5EDgo/create-unitypackage@master - with: - package-path: 'NativeWebSocket.unitypackage' - include-files: metaList - - name: Upload Package - uses: actions/upload-artifact@master - with: - path: ./NativeWebSocket.unitypackage - name: NativeWebSocket - - name: Changelog - uses: scottbrenner/generate-changelog-action@master - id: Changelog - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.version.outputs.prop }} - release_name: ${{ steps.version.outputs.prop }} - body: | - ${{ steps.Changelog.outputs.changelog }} - draft: false - prerelease: true - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - asset_path: ./NativeWebSocket.unitypackage - asset_name: NativeWebSocket.unitypackage - asset_content_type: application/x-gzip diff --git a/.gitignore b/.gitignore index 1fbd435..d48e0ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .DS_Store node_modules +bin/ +obj/ # This .gitignore file should be placed at the root of your Unity project directory # @@ -34,6 +36,11 @@ ExportedObj/ *.csproj *.unityproj *.sln + +# Don't ignore project files for the core library and integrations +!src/**/*.csproj +!integrations/**/*.csproj +!NativeWebSocket.sln *.suo *.tmp *.user @@ -60,3 +67,4 @@ sysinfo.txt # Crashlytics generated file crashlytics-build.properties +.godot diff --git a/Media/header.png b/Media/header.png index 7352b48..d2465a8 100644 Binary files a/Media/header.png and b/Media/header.png differ diff --git a/NativeWebSocket.sln b/NativeWebSocket.sln new file mode 100644 index 0000000..25e22b7 --- /dev/null +++ b/NativeWebSocket.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeWebSocket", "src\NativeWebSocket\NativeWebSocket.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeWebSocket.MonoGame", "integrations\MonoGame\NativeWebSocket.MonoGame.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGameExample", "examples\MonoGame\MonoGameExample.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/NativeWebSocket/Assets/Samples~/WebSocketExample/Connection.cs b/NativeWebSocket/Assets/Samples~/WebSocketExample/Connection.cs deleted file mode 100644 index 32acbd8..0000000 --- a/NativeWebSocket/Assets/Samples~/WebSocketExample/Connection.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -using NativeWebSocket; - -public class Connection : MonoBehaviour -{ - WebSocket websocket; - - // Start is called before the first frame update - async void Start() - { - // websocket = new WebSocket("ws://echo.websocket.org"); - websocket = new WebSocket("ws://localhost:3000"); - - websocket.OnOpen += () => - { - Debug.Log("Connection open!"); - }; - - websocket.OnError += (e) => - { - Debug.Log("Error! " + e); - }; - - websocket.OnClose += (e) => - { - Debug.Log("Connection closed!"); - }; - - websocket.OnMessage += (bytes) => - { - // Reading a plain text message - var message = System.Text.Encoding.UTF8.GetString(bytes); - Debug.Log("Received OnMessage! (" + bytes.Length + " bytes) " + message); - }; - - // Keep sending messages at every 0.3s - InvokeRepeating("SendWebSocketMessage", 0.0f, 0.3f); - - await websocket.Connect(); - } - - void Update() - { - #if !UNITY_WEBGL || UNITY_EDITOR - websocket.DispatchMessageQueue(); - #endif - } - - async void SendWebSocketMessage() - { - if (websocket.State == WebSocketState.Open) - { - // Sending bytes - await websocket.Send(new byte[] { 10, 20, 30 }); - - // Sending plain text - await websocket.SendText("plain text message"); - } - } - - private async void OnApplicationQuit() - { - await websocket.Close(); - } -} diff --git a/NativeWebSocket/Assets/Samples~/WebSocketExample/Connection.cs.meta b/NativeWebSocket/Assets/Samples~/WebSocketExample/Connection.cs.meta deleted file mode 100644 index 6ad36a6..0000000 --- a/NativeWebSocket/Assets/Samples~/WebSocketExample/Connection.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0f451fab1e6d94aea8fb73803e281bb3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/Samples~/WebSocketExample/WebSocketExampleScene.unity b/NativeWebSocket/Assets/Samples~/WebSocketExample/WebSocketExampleScene.unity deleted file mode 100644 index c47fb7f..0000000 --- a/NativeWebSocket/Assets/Samples~/WebSocketExample/WebSocketExampleScene.unity +++ /dev/null @@ -1,326 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!29 &1 -OcclusionCullingSettings: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_OcclusionBakeSettings: - smallestOccluder: 5 - smallestHole: 0.25 - backfaceThreshold: 100 - m_SceneGUID: 00000000000000000000000000000000 - m_OcclusionCullingData: {fileID: 0} ---- !u!104 &2 -RenderSettings: - m_ObjectHideFlags: 0 - serializedVersion: 9 - m_Fog: 0 - m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} - m_FogMode: 3 - m_FogDensity: 0.01 - m_LinearFogStart: 0 - m_LinearFogEnd: 300 - m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} - m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} - m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} - m_AmbientIntensity: 1 - m_AmbientMode: 0 - m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} - m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} - m_HaloStrength: 0.5 - m_FlareStrength: 1 - m_FlareFadeSpeed: 3 - m_HaloTexture: {fileID: 0} - m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} - m_DefaultReflectionMode: 0 - m_DefaultReflectionResolution: 128 - m_ReflectionBounces: 1 - m_ReflectionIntensity: 1 - m_CustomReflection: {fileID: 0} - m_Sun: {fileID: 705507994} - m_IndirectSpecularColor: {r: 0.44657868, g: 0.49641263, b: 0.57481706, a: 1} - m_UseRadianceAmbientProbe: 0 ---- !u!157 &3 -LightmapSettings: - m_ObjectHideFlags: 0 - serializedVersion: 11 - m_GIWorkflowMode: 0 - m_GISettings: - serializedVersion: 2 - m_BounceScale: 1 - m_IndirectOutputScale: 1 - m_AlbedoBoost: 1 - m_EnvironmentLightingMode: 0 - m_EnableBakedLightmaps: 1 - m_EnableRealtimeLightmaps: 1 - m_LightmapEditorSettings: - serializedVersion: 12 - m_Resolution: 2 - m_BakeResolution: 40 - m_AtlasSize: 1024 - m_AO: 0 - m_AOMaxDistance: 1 - m_CompAOExponent: 1 - m_CompAOExponentDirect: 0 - m_ExtractAmbientOcclusion: 0 - m_Padding: 2 - m_LightmapParameters: {fileID: 0} - m_LightmapsBakeMode: 1 - m_TextureCompression: 1 - m_FinalGather: 0 - m_FinalGatherFiltering: 1 - m_FinalGatherRayCount: 256 - m_ReflectionCompression: 2 - m_MixedBakeMode: 2 - m_BakeBackend: 1 - m_PVRSampling: 1 - m_PVRDirectSampleCount: 32 - m_PVRSampleCount: 500 - m_PVRBounces: 2 - m_PVREnvironmentSampleCount: 500 - m_PVREnvironmentReferencePointCount: 2048 - m_PVRFilteringMode: 2 - m_PVRDenoiserTypeDirect: 0 - m_PVRDenoiserTypeIndirect: 0 - m_PVRDenoiserTypeAO: 0 - m_PVRFilterTypeDirect: 0 - m_PVRFilterTypeIndirect: 0 - m_PVRFilterTypeAO: 0 - m_PVREnvironmentMIS: 0 - m_PVRCulling: 1 - m_PVRFilteringGaussRadiusDirect: 1 - m_PVRFilteringGaussRadiusIndirect: 5 - m_PVRFilteringGaussRadiusAO: 2 - m_PVRFilteringAtrousPositionSigmaDirect: 0.5 - m_PVRFilteringAtrousPositionSigmaIndirect: 2 - m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ExportTrainingData: 0 - m_TrainingDataDestination: TrainingData - m_LightingDataAsset: {fileID: 0} - m_UseShadowmask: 1 ---- !u!196 &4 -NavMeshSettings: - serializedVersion: 2 - m_ObjectHideFlags: 0 - m_BuildSettings: - serializedVersion: 2 - agentTypeID: 0 - agentRadius: 0.5 - agentHeight: 2 - agentSlope: 45 - agentClimb: 0.4 - ledgeDropHeight: 0 - maxJumpAcrossDistance: 0 - minRegionArea: 2 - manualCellSize: 0 - cellSize: 0.16666667 - manualTileSize: 0 - tileSize: 256 - accuratePlacement: 0 - debug: - m_Flags: 0 - m_NavMeshData: {fileID: 0} ---- !u!1 &705507993 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 705507995} - - component: {fileID: 705507994} - m_Layer: 0 - m_Name: Directional Light - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!108 &705507994 -Light: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 705507993} - m_Enabled: 1 - serializedVersion: 9 - m_Type: 1 - m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} - m_Intensity: 1 - m_Range: 10 - m_SpotAngle: 30 - m_InnerSpotAngle: 21.802082 - m_CookieSize: 10 - m_Shadows: - m_Type: 2 - m_Resolution: -1 - m_CustomResolution: -1 - m_Strength: 1 - m_Bias: 0.05 - m_NormalBias: 0.4 - m_NearPlane: 0.2 - m_CullingMatrixOverride: - e00: 1 - e01: 0 - e02: 0 - e03: 0 - e10: 0 - e11: 1 - e12: 0 - e13: 0 - e20: 0 - e21: 0 - e22: 1 - e23: 0 - e30: 0 - e31: 0 - e32: 0 - e33: 1 - m_UseCullingMatrixOverride: 0 - m_Cookie: {fileID: 0} - m_DrawHalo: 0 - m_Flare: {fileID: 0} - m_RenderMode: 0 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingLayerMask: 1 - m_Lightmapping: 1 - m_LightShadowCasterMode: 0 - m_AreaSize: {x: 1, y: 1} - m_BounceIntensity: 1 - m_ColorTemperature: 6570 - m_UseColorTemperature: 0 - m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} - m_UseBoundingSphereOverride: 0 - m_ShadowRadius: 0 - m_ShadowAngle: 0 ---- !u!4 &705507995 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 705507993} - m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} - m_LocalPosition: {x: 0, y: 3, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} ---- !u!1 &963194225 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 963194228} - - component: {fileID: 963194227} - - component: {fileID: 963194226} - - component: {fileID: 963194230} - - component: {fileID: 963194229} - m_Layer: 0 - m_Name: Main Camera - m_TagString: MainCamera - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!81 &963194226 -AudioListener: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - m_Enabled: 1 ---- !u!20 &963194227 -Camera: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - m_Enabled: 1 - serializedVersion: 2 - m_ClearFlags: 1 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} - m_projectionMatrixMode: 1 - m_GateFitMode: 2 - m_FOVAxisMode: 0 - m_SensorSize: {x: 36, y: 24} - m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 - m_NormalizedViewPortRect: - serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 - near clip plane: 0.3 - far clip plane: 1000 - field of view: 60 - orthographic: 0 - orthographic size: 5 - m_Depth: -1 - m_CullingMask: - serializedVersion: 2 - m_Bits: 4294967295 - m_RenderingPath: -1 - m_TargetTexture: {fileID: 0} - m_TargetDisplay: 0 - m_TargetEye: 3 - m_HDR: 1 - m_AllowMSAA: 1 - m_AllowDynamicResolution: 0 - m_ForceIntoRT: 0 - m_OcclusionCulling: 1 - m_StereoConvergence: 10 - m_StereoSeparation: 0.022 ---- !u!4 &963194228 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1, z: -10} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &963194229 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 0f451fab1e6d94aea8fb73803e281bb3, type: 3} - m_Name: - m_EditorClassIdentifier: ---- !u!54 &963194230 -Rigidbody: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - serializedVersion: 2 - m_Mass: 1 - m_Drag: 0 - m_AngularDrag: 0.05 - m_UseGravity: 1 - m_IsKinematic: 0 - m_Interpolate: 0 - m_Constraints: 0 - m_CollisionDetection: 0 diff --git a/NativeWebSocket/Assets/Samples~/WebSocketExample/WebSocketExampleScene.unity.meta b/NativeWebSocket/Assets/Samples~/WebSocketExample/WebSocketExampleScene.unity.meta deleted file mode 100644 index 952bd1e..0000000 --- a/NativeWebSocket/Assets/Samples~/WebSocketExample/WebSocketExampleScene.unity.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 9fc0d4010bbf28b4594072e72b8655ab -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/WebSocket.meta b/NativeWebSocket/Assets/WebSocket.meta deleted file mode 100644 index 5b5dd00..0000000 --- a/NativeWebSocket/Assets/WebSocket.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 1b0aac75ea12c4d6d8aaf04e069a69a7 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/WebSocket/WebSocket.cs b/NativeWebSocket/Assets/WebSocket/WebSocket.cs deleted file mode 100644 index 956fa21..0000000 --- a/NativeWebSocket/Assets/WebSocket/WebSocket.cs +++ /dev/null @@ -1,848 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.WebSockets; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -using AOT; -using System.Runtime.InteropServices; -using UnityEngine; -using System.Collections; - -public class MainThreadUtil : MonoBehaviour -{ - public static MainThreadUtil Instance { get; private set; } - public static SynchronizationContext synchronizationContext { get; private set; } - - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] - public static void Setup() - { - Instance = new GameObject("MainThreadUtil") - .AddComponent(); - synchronizationContext = SynchronizationContext.Current; - } - - public static void Run(IEnumerator waitForUpdate) - { - synchronizationContext.Post(_ => Instance.StartCoroutine( - waitForUpdate), null); - } - - void Awake() - { - gameObject.hideFlags = HideFlags.HideAndDontSave; - DontDestroyOnLoad(gameObject); - } -} - -public class WaitForUpdate : CustomYieldInstruction -{ - public override bool keepWaiting - { - get { return false; } - } - - public MainThreadAwaiter GetAwaiter() - { - var awaiter = new MainThreadAwaiter(); - MainThreadUtil.Run(CoroutineWrapper(this, awaiter)); - return awaiter; - } - - public class MainThreadAwaiter : INotifyCompletion - { - Action continuation; - - public bool IsCompleted { get; set; } - - public void GetResult() { } - - public void Complete() - { - IsCompleted = true; - continuation?.Invoke(); - } - - void INotifyCompletion.OnCompleted(Action continuation) - { - this.continuation = continuation; - } - } - - public static IEnumerator CoroutineWrapper(IEnumerator theWorker, MainThreadAwaiter awaiter) - { - yield return theWorker; - awaiter.Complete(); - } -} - -namespace NativeWebSocket -{ - public delegate void WebSocketOpenEventHandler(); - public delegate void WebSocketMessageEventHandler(byte[] data); - public delegate void WebSocketErrorEventHandler(string errorMsg); - public delegate void WebSocketCloseEventHandler(WebSocketCloseCode closeCode); - - public enum WebSocketCloseCode - { - /* Do NOT use NotSet - it's only purpose is to indicate that the close code cannot be parsed. */ - NotSet = 0, - Normal = 1000, - Away = 1001, - ProtocolError = 1002, - UnsupportedData = 1003, - Undefined = 1004, - NoStatus = 1005, - Abnormal = 1006, - InvalidData = 1007, - PolicyViolation = 1008, - TooBig = 1009, - MandatoryExtension = 1010, - ServerError = 1011, - TlsHandshakeFailure = 1015 - } - - public enum WebSocketState - { - Connecting, - Open, - Closing, - Closed - } - - public interface IWebSocket - { - event WebSocketOpenEventHandler OnOpen; - event WebSocketMessageEventHandler OnMessage; - event WebSocketErrorEventHandler OnError; - event WebSocketCloseEventHandler OnClose; - - WebSocketState State { get; } - } - - public static class WebSocketHelpers - { - public static WebSocketCloseCode ParseCloseCodeEnum(int closeCode) - { - - if (WebSocketCloseCode.IsDefined(typeof(WebSocketCloseCode), closeCode)) - { - return (WebSocketCloseCode)closeCode; - } - else - { - return WebSocketCloseCode.Undefined; - } - - } - - public static WebSocketException GetErrorMessageFromCode(int errorCode, Exception inner) - { - switch (errorCode) - { - case -1: - return new WebSocketUnexpectedException("WebSocket instance not found.", inner); - case -2: - return new WebSocketInvalidStateException("WebSocket is already connected or in connecting state.", inner); - case -3: - return new WebSocketInvalidStateException("WebSocket is not connected.", inner); - case -4: - return new WebSocketInvalidStateException("WebSocket is already closing.", inner); - case -5: - return new WebSocketInvalidStateException("WebSocket is already closed.", inner); - case -6: - return new WebSocketInvalidStateException("WebSocket is not in open state.", inner); - case -7: - return new WebSocketInvalidArgumentException("Cannot close WebSocket. An invalid code was specified or reason is too long.", inner); - default: - return new WebSocketUnexpectedException("Unknown error.", inner); - } - } - } - - public class WebSocketException : Exception - { - public WebSocketException() { } - public WebSocketException(string message) : base(message) { } - public WebSocketException(string message, Exception inner) : base(message, inner) { } - } - - public class WebSocketUnexpectedException : WebSocketException - { - public WebSocketUnexpectedException() { } - public WebSocketUnexpectedException(string message) : base(message) { } - public WebSocketUnexpectedException(string message, Exception inner) : base(message, inner) { } - } - - public class WebSocketInvalidArgumentException : WebSocketException - { - public WebSocketInvalidArgumentException() { } - public WebSocketInvalidArgumentException(string message) : base(message) { } - public WebSocketInvalidArgumentException(string message, Exception inner) : base(message, inner) { } - } - - public class WebSocketInvalidStateException : WebSocketException - { - public WebSocketInvalidStateException() { } - public WebSocketInvalidStateException(string message) : base(message) { } - public WebSocketInvalidStateException(string message, Exception inner) : base(message, inner) { } - } - - public class WaitForBackgroundThread - { - public ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter() - { - return Task.Run(() => { }).ConfigureAwait(false).GetAwaiter(); - } - } - -#if UNITY_WEBGL && !UNITY_EDITOR - - /// - /// WebSocket class bound to JSLIB. - /// - public class WebSocket : IWebSocket { - - /* WebSocket JSLIB functions */ - [DllImport ("__Internal")] - public static extern int WebSocketConnect (int instanceId); - - [DllImport ("__Internal")] - public static extern int WebSocketClose (int instanceId, int code, string reason); - - [DllImport ("__Internal")] - public static extern int WebSocketSend (int instanceId, byte[] dataPtr, int dataLength); - - [DllImport ("__Internal")] - public static extern int WebSocketSendText (int instanceId, string message); - - [DllImport ("__Internal")] - public static extern int WebSocketGetState (int instanceId); - - protected int instanceId; - - public event WebSocketOpenEventHandler OnOpen; - public event WebSocketMessageEventHandler OnMessage; - public event WebSocketErrorEventHandler OnError; - public event WebSocketCloseEventHandler OnClose; - - public WebSocket (string url, Dictionary headers = null) { - if (!WebSocketFactory.isInitialized) { - WebSocketFactory.Initialize (); - } - - int instanceId = WebSocketFactory.WebSocketAllocate (url); - WebSocketFactory.instances.Add (instanceId, this); - - this.instanceId = instanceId; - } - - public WebSocket (string url, string subprotocol, Dictionary headers = null) { - if (!WebSocketFactory.isInitialized) { - WebSocketFactory.Initialize (); - } - - int instanceId = WebSocketFactory.WebSocketAllocate (url); - WebSocketFactory.instances.Add (instanceId, this); - - WebSocketFactory.WebSocketAddSubProtocol(instanceId, subprotocol); - - this.instanceId = instanceId; - } - - public WebSocket (string url, List subprotocols, Dictionary headers = null) { - if (!WebSocketFactory.isInitialized) { - WebSocketFactory.Initialize (); - } - - int instanceId = WebSocketFactory.WebSocketAllocate (url); - WebSocketFactory.instances.Add (instanceId, this); - - foreach (string subprotocol in subprotocols) { - WebSocketFactory.WebSocketAddSubProtocol(instanceId, subprotocol); - } - - this.instanceId = instanceId; - } - - ~WebSocket () { - WebSocketFactory.HandleInstanceDestroy (this.instanceId); - } - - public int GetInstanceId () { - return this.instanceId; - } - - public Task Connect () { - int ret = WebSocketConnect (this.instanceId); - - if (ret < 0) - throw WebSocketHelpers.GetErrorMessageFromCode (ret, null); - - return Task.CompletedTask; - } - - public void CancelConnection () { - if (State == WebSocketState.Open) - Close (WebSocketCloseCode.Abnormal); - } - - public Task Close (WebSocketCloseCode code = WebSocketCloseCode.Normal, string reason = null) { - int ret = WebSocketClose (this.instanceId, (int) code, reason); - - if (ret < 0) - throw WebSocketHelpers.GetErrorMessageFromCode (ret, null); - - return Task.CompletedTask; - } - - public Task Send (byte[] data) { - int ret = WebSocketSend (this.instanceId, data, data.Length); - - if (ret < 0) - throw WebSocketHelpers.GetErrorMessageFromCode (ret, null); - - return Task.CompletedTask; - } - - public Task SendText (string message) { - int ret = WebSocketSendText (this.instanceId, message); - - if (ret < 0) - throw WebSocketHelpers.GetErrorMessageFromCode (ret, null); - - return Task.CompletedTask; - } - - public WebSocketState State { - get { - int state = WebSocketGetState (this.instanceId); - - if (state < 0) - throw WebSocketHelpers.GetErrorMessageFromCode (state, null); - - switch (state) { - case 0: - return WebSocketState.Connecting; - - case 1: - return WebSocketState.Open; - - case 2: - return WebSocketState.Closing; - - case 3: - return WebSocketState.Closed; - - default: - return WebSocketState.Closed; - } - } - } - - public void DelegateOnOpenEvent () { - this.OnOpen?.Invoke (); - } - - public void DelegateOnMessageEvent (byte[] data) { - this.OnMessage?.Invoke (data); - } - - public void DelegateOnErrorEvent (string errorMsg) { - this.OnError?.Invoke (errorMsg); - } - - public void DelegateOnCloseEvent (int closeCode) { - this.OnClose?.Invoke (WebSocketHelpers.ParseCloseCodeEnum (closeCode)); - } - - } - -#else - - public class WebSocket : IWebSocket - { - public event WebSocketOpenEventHandler OnOpen; - public event WebSocketMessageEventHandler OnMessage; - public event WebSocketErrorEventHandler OnError; - public event WebSocketCloseEventHandler OnClose; - - private Uri uri; - private Dictionary headers; - private List subprotocols; - private ClientWebSocket m_Socket = new ClientWebSocket(); - - private CancellationTokenSource m_TokenSource; - private CancellationToken m_CancellationToken; - - private readonly object OutgoingMessageLock = new object(); - private readonly object IncomingMessageLock = new object(); - - private bool isSending = false; - private List> sendBytesQueue = new List>(); - private List> sendTextQueue = new List>(); - - public WebSocket(string url, Dictionary headers = null) - { - uri = new Uri(url); - - if (headers == null) - { - this.headers = new Dictionary(); - } - else - { - this.headers = headers; - } - - subprotocols = new List(); - - string protocol = uri.Scheme; - if (!protocol.Equals("ws") && !protocol.Equals("wss")) - throw new ArgumentException("Unsupported protocol: " + protocol); - } - - public WebSocket(string url, string subprotocol, Dictionary headers = null) - { - uri = new Uri(url); - - if (headers == null) - { - this.headers = new Dictionary(); - } - else - { - this.headers = headers; - } - - subprotocols = new List {subprotocol}; - - string protocol = uri.Scheme; - if (!protocol.Equals("ws") && !protocol.Equals("wss")) - throw new ArgumentException("Unsupported protocol: " + protocol); - } - - public WebSocket(string url, List subprotocols, Dictionary headers = null) - { - uri = new Uri(url); - - if (headers == null) - { - this.headers = new Dictionary(); - } - else - { - this.headers = headers; - } - - this.subprotocols = subprotocols; - - string protocol = uri.Scheme; - if (!protocol.Equals("ws") && !protocol.Equals("wss")) - throw new ArgumentException("Unsupported protocol: " + protocol); - } - - public void CancelConnection() - { - m_TokenSource?.Cancel(); - } - - public async Task Connect() - { - try - { - m_TokenSource = new CancellationTokenSource(); - m_CancellationToken = m_TokenSource.Token; - - m_Socket = new ClientWebSocket(); - - foreach (var header in headers) - { - m_Socket.Options.SetRequestHeader(header.Key, header.Value); - } - - foreach (string subprotocol in subprotocols) { - m_Socket.Options.AddSubProtocol(subprotocol); - } - - await m_Socket.ConnectAsync(uri, m_CancellationToken); - OnOpen?.Invoke(); - - await Receive(); - } - catch (Exception ex) - { - OnError?.Invoke(ex.Message); - OnClose?.Invoke(WebSocketCloseCode.Abnormal); - } - finally - { - if (m_Socket != null) - { - m_TokenSource.Cancel(); - m_Socket.Dispose(); - } - } - } - - public WebSocketState State - { - get - { - switch (m_Socket.State) - { - case System.Net.WebSockets.WebSocketState.Connecting: - return WebSocketState.Connecting; - - case System.Net.WebSockets.WebSocketState.Open: - return WebSocketState.Open; - - case System.Net.WebSockets.WebSocketState.CloseSent: - case System.Net.WebSockets.WebSocketState.CloseReceived: - return WebSocketState.Closing; - - case System.Net.WebSockets.WebSocketState.Closed: - return WebSocketState.Closed; - - default: - return WebSocketState.Closed; - } - } - } - - public Task Send(byte[] bytes) - { - // return m_Socket.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None); - return SendMessage(sendBytesQueue, WebSocketMessageType.Binary, new ArraySegment(bytes)); - } - - public Task SendText(string message) - { - var encoded = Encoding.UTF8.GetBytes(message); - - // m_Socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); - return SendMessage(sendTextQueue, WebSocketMessageType.Text, new ArraySegment(encoded, 0, encoded.Length)); - } - - private async Task SendMessage(List> queue, WebSocketMessageType messageType, ArraySegment buffer) - { - // Return control to the calling method immediately. - // await Task.Yield (); - - // Make sure we have data. - if (buffer.Count == 0) - { - return; - } - - // The state of the connection is contained in the context Items dictionary. - bool sending; - - lock (OutgoingMessageLock) - { - sending = isSending; - - // If not, we are now. - if (!isSending) - { - isSending = true; - } - } - - if (!sending) - { - // Lock with a timeout, just in case. - if (!Monitor.TryEnter(m_Socket, 1000)) - { - // If we couldn't obtain exclusive access to the socket in one second, something is wrong. - await m_Socket.CloseAsync(WebSocketCloseStatus.InternalServerError, string.Empty, m_CancellationToken); - return; - } - - try - { - // Send the message synchronously. - var t = m_Socket.SendAsync(buffer, messageType, true, m_CancellationToken); - t.Wait(m_CancellationToken); - } - finally - { - Monitor.Exit(m_Socket); - - // Note that we've finished sending. - lock (OutgoingMessageLock) - { - isSending = false; - } - } - - // Handle any queued messages. - await HandleQueue(queue, messageType); - } - else - { - // Add the message to the queue. - lock (OutgoingMessageLock) - { - queue.Add(buffer); - } - } - } - - private async Task HandleQueue(List> queue, WebSocketMessageType messageType) - { - var buffer = new ArraySegment(); - lock (OutgoingMessageLock) - { - // Check for an item in the queue. - if (queue.Count > 0) - { - // Pull it off the top. - buffer = queue[0]; - queue.RemoveAt(0); - } - } - - // Send that message. - if (buffer.Count > 0) - { - await SendMessage(queue, messageType, buffer); - } - } - - private List m_MessageList = new List(); - - // simple dispatcher for queued messages. - public void DispatchMessageQueue() - { - if (m_MessageList.Count == 0) - { - return; - } - - List messageListCopy; - - lock (IncomingMessageLock) - { - messageListCopy = new List(m_MessageList); - m_MessageList.Clear(); - } - - var len = messageListCopy.Count; - for (int i = 0; i < len; i++) - { - OnMessage?.Invoke(messageListCopy[i]); - } - } - - public async Task Receive() - { - WebSocketCloseCode closeCode = WebSocketCloseCode.Abnormal; - await new WaitForBackgroundThread(); - - ArraySegment buffer = new ArraySegment(new byte[8192]); - try - { - while (m_Socket.State == System.Net.WebSockets.WebSocketState.Open) - { - WebSocketReceiveResult result = null; - - using (var ms = new MemoryStream()) - { - do - { - result = await m_Socket.ReceiveAsync(buffer, m_CancellationToken); - ms.Write(buffer.Array, buffer.Offset, result.Count); - } - while (!result.EndOfMessage); - - ms.Seek(0, SeekOrigin.Begin); - - if (result.MessageType == WebSocketMessageType.Text) - { - lock (IncomingMessageLock) - { - m_MessageList.Add(ms.ToArray()); - } - - //using (var reader = new StreamReader(ms, Encoding.UTF8)) - //{ - // string message = reader.ReadToEnd(); - // OnMessage?.Invoke(this, new MessageEventArgs(message)); - //} - } - else if (result.MessageType == WebSocketMessageType.Binary) - { - lock (IncomingMessageLock) - { - m_MessageList.Add(ms.ToArray()); - } - } - else if (result.MessageType == WebSocketMessageType.Close) - { - await Close(); - closeCode = WebSocketHelpers.ParseCloseCodeEnum((int)result.CloseStatus); - break; - } - } - } - } - catch (Exception) - { - m_TokenSource.Cancel(); - } - finally - { - await new WaitForUpdate(); - OnClose?.Invoke(closeCode); - } - } - - public async Task Close() - { - if (State == WebSocketState.Open) - { - await m_Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, m_CancellationToken); - } - } - } -#endif - - /// - /// Factory - /// - - /// - /// Class providing static access methods to work with JSLIB WebSocket or WebSocketSharp interface - /// - public static class WebSocketFactory - { - -#if UNITY_WEBGL && !UNITY_EDITOR - /* Map of websocket instances */ - public static Dictionary instances = new Dictionary (); - - /* Delegates */ - public delegate void OnOpenCallback (int instanceId); - public delegate void OnMessageCallback (int instanceId, System.IntPtr msgPtr, int msgSize); - public delegate void OnErrorCallback (int instanceId, System.IntPtr errorPtr); - public delegate void OnCloseCallback (int instanceId, int closeCode); - - /* WebSocket JSLIB callback setters and other functions */ - [DllImport ("__Internal")] - public static extern int WebSocketAllocate (string url); - - [DllImport ("__Internal")] - public static extern int WebSocketAddSubProtocol (int instanceId, string subprotocol); - - [DllImport ("__Internal")] - public static extern void WebSocketFree (int instanceId); - - [DllImport ("__Internal")] - public static extern void WebSocketSetOnOpen (OnOpenCallback callback); - - [DllImport ("__Internal")] - public static extern void WebSocketSetOnMessage (OnMessageCallback callback); - - [DllImport ("__Internal")] - public static extern void WebSocketSetOnError (OnErrorCallback callback); - - [DllImport ("__Internal")] - public static extern void WebSocketSetOnClose (OnCloseCallback callback); - - /* If callbacks was initialized and set */ - public static bool isInitialized = false; - - /* - * Initialize WebSocket callbacks to JSLIB - */ - public static void Initialize () { - - WebSocketSetOnOpen (DelegateOnOpenEvent); - WebSocketSetOnMessage (DelegateOnMessageEvent); - WebSocketSetOnError (DelegateOnErrorEvent); - WebSocketSetOnClose (DelegateOnCloseEvent); - - isInitialized = true; - - } - - /// - /// Called when instance is destroyed (by destructor) - /// Method removes instance from map and free it in JSLIB implementation - /// - /// Instance identifier. - public static void HandleInstanceDestroy (int instanceId) { - - instances.Remove (instanceId); - WebSocketFree (instanceId); - - } - - [MonoPInvokeCallback (typeof (OnOpenCallback))] - public static void DelegateOnOpenEvent (int instanceId) { - - WebSocket instanceRef; - - if (instances.TryGetValue (instanceId, out instanceRef)) { - instanceRef.DelegateOnOpenEvent (); - } - - } - - [MonoPInvokeCallback (typeof (OnMessageCallback))] - public static void DelegateOnMessageEvent (int instanceId, System.IntPtr msgPtr, int msgSize) { - - WebSocket instanceRef; - - if (instances.TryGetValue (instanceId, out instanceRef)) { - byte[] msg = new byte[msgSize]; - Marshal.Copy (msgPtr, msg, 0, msgSize); - - instanceRef.DelegateOnMessageEvent (msg); - } - - } - - [MonoPInvokeCallback (typeof (OnErrorCallback))] - public static void DelegateOnErrorEvent (int instanceId, System.IntPtr errorPtr) { - - WebSocket instanceRef; - - if (instances.TryGetValue (instanceId, out instanceRef)) { - - string errorMsg = Marshal.PtrToStringAuto (errorPtr); - instanceRef.DelegateOnErrorEvent (errorMsg); - - } - - } - - [MonoPInvokeCallback (typeof (OnCloseCallback))] - public static void DelegateOnCloseEvent (int instanceId, int closeCode) { - - WebSocket instanceRef; - - if (instances.TryGetValue (instanceId, out instanceRef)) { - instanceRef.DelegateOnCloseEvent (closeCode); - } - - } -#endif - - /// - /// Create WebSocket client instance - /// - /// The WebSocket instance. - /// WebSocket valid URL. - public static WebSocket CreateInstance(string url) - { - return new WebSocket(url); - } - - } - -} diff --git a/NativeWebSocket/Assets/WebSocket/WebSocket.cs.meta b/NativeWebSocket/Assets/WebSocket/WebSocket.cs.meta deleted file mode 100644 index 708e6f1..0000000 --- a/NativeWebSocket/Assets/WebSocket/WebSocket.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: df0e653fbf9005342904aa0f14d46088 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/WebSocket/WebSocket.jslib.meta b/NativeWebSocket/Assets/WebSocket/WebSocket.jslib.meta deleted file mode 100644 index a4f0052..0000000 --- a/NativeWebSocket/Assets/WebSocket/WebSocket.jslib.meta +++ /dev/null @@ -1,32 +0,0 @@ -fileFormatVersion: 2 -guid: 3b767cc116f846f4b813fe00142b8736 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - WebGL: WebGL - second: - enabled: 1 - settings: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/WebSocket/WebSocket.jspre.meta b/NativeWebSocket/Assets/WebSocket/WebSocket.jspre.meta deleted file mode 100644 index fb05d9b..0000000 --- a/NativeWebSocket/Assets/WebSocket/WebSocket.jspre.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 267dd868ba98c4220b28f212fc144f59 \ No newline at end of file diff --git a/NativeWebSocket/Assets/WebSocket/endel.nativewebsocket.asmdef.meta b/NativeWebSocket/Assets/WebSocket/endel.nativewebsocket.asmdef.meta deleted file mode 100644 index 01d2d89..0000000 --- a/NativeWebSocket/Assets/WebSocket/endel.nativewebsocket.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 04376767bc1f3b428aefa3d20743e819 -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/WebSocketExample.meta b/NativeWebSocket/Assets/WebSocketExample.meta deleted file mode 100644 index fecdc8e..0000000 --- a/NativeWebSocket/Assets/WebSocketExample.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e7a91621cd77941248ad2d795977b3c7 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Assets/package.json.meta b/NativeWebSocket/Assets/package.json.meta deleted file mode 100644 index 401838d..0000000 --- a/NativeWebSocket/Assets/package.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: a9f6363663f95e3499b2616618078182 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NativeWebSocket/Packages/manifest.json b/NativeWebSocket/Packages/manifest.json deleted file mode 100644 index 781bbaa..0000000 --- a/NativeWebSocket/Packages/manifest.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "dependencies": { - "com.unity.ai.navigation": "2.0.9", - "com.unity.collab-proxy": "2.11.2", - "com.unity.ext.nunit": "2.0.5", - "com.unity.ide.rider": "3.0.38", - "com.unity.ide.visualstudio": "2.0.26", - "com.unity.ide.vscode": "1.2.4", - "com.unity.multiplayer.center": "1.0.1", - "com.unity.test-framework": "1.6.0", - "com.unity.timeline": "1.8.10", - "com.unity.ugui": "2.0.0", - "com.unity.modules.accessibility": "1.0.0", - "com.unity.modules.adaptiveperformance": "1.0.0", - "com.unity.modules.ai": "1.0.0", - "com.unity.modules.androidjni": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.cloth": "1.0.0", - "com.unity.modules.director": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.physics2d": "1.0.0", - "com.unity.modules.screencapture": "1.0.0", - "com.unity.modules.terrain": "1.0.0", - "com.unity.modules.terrainphysics": "1.0.0", - "com.unity.modules.tilemap": "1.0.0", - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.uielements": "1.0.0", - "com.unity.modules.umbra": "1.0.0", - "com.unity.modules.unityanalytics": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.unitywebrequesttexture": "1.0.0", - "com.unity.modules.unitywebrequestwww": "1.0.0", - "com.unity.modules.vectorgraphics": "1.0.0", - "com.unity.modules.vehicles": "1.0.0", - "com.unity.modules.video": "1.0.0", - "com.unity.modules.vr": "1.0.0", - "com.unity.modules.wind": "1.0.0", - "com.unity.modules.xr": "1.0.0" - } -} diff --git a/NativeWebSocket/ProjectSettings/AudioManager.asset b/NativeWebSocket/ProjectSettings/AudioManager.asset deleted file mode 100644 index 07ebfb0..0000000 --- a/NativeWebSocket/ProjectSettings/AudioManager.asset +++ /dev/null @@ -1,19 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!11 &1 -AudioManager: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Volume: 1 - Rolloff Scale: 1 - Doppler Factor: 1 - Default Speaker Mode: 2 - m_SampleRate: 0 - m_DSPBufferSize: 1024 - m_VirtualVoiceCount: 512 - m_RealVoiceCount: 32 - m_SpatializerPlugin: - m_AmbisonicDecoderPlugin: - m_DisableAudio: 0 - m_VirtualizeEffects: 1 - m_RequestedDSPBufferSize: 1024 diff --git a/NativeWebSocket/ProjectSettings/ClusterInputManager.asset b/NativeWebSocket/ProjectSettings/ClusterInputManager.asset deleted file mode 100644 index e7886b2..0000000 --- a/NativeWebSocket/ProjectSettings/ClusterInputManager.asset +++ /dev/null @@ -1,6 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!236 &1 -ClusterInputManager: - m_ObjectHideFlags: 0 - m_Inputs: [] diff --git a/NativeWebSocket/ProjectSettings/DynamicsManager.asset b/NativeWebSocket/ProjectSettings/DynamicsManager.asset deleted file mode 100644 index cdc1f3e..0000000 --- a/NativeWebSocket/ProjectSettings/DynamicsManager.asset +++ /dev/null @@ -1,34 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!55 &1 -PhysicsManager: - m_ObjectHideFlags: 0 - serializedVersion: 11 - m_Gravity: {x: 0, y: -9.81, z: 0} - m_DefaultMaterial: {fileID: 0} - m_BounceThreshold: 2 - m_SleepThreshold: 0.005 - m_DefaultContactOffset: 0.01 - m_DefaultSolverIterations: 6 - m_DefaultSolverVelocityIterations: 1 - m_QueriesHitBackfaces: 0 - m_QueriesHitTriggers: 1 - m_EnableAdaptiveForce: 0 - m_ClothInterCollisionDistance: 0 - m_ClothInterCollisionStiffness: 0 - m_ContactsGeneration: 1 - m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - m_AutoSimulation: 1 - m_AutoSyncTransforms: 0 - m_ReuseCollisionCallbacks: 1 - m_ClothInterCollisionSettingsToggle: 0 - m_ContactPairsMode: 0 - m_BroadphaseType: 0 - m_WorldBounds: - m_Center: {x: 0, y: 0, z: 0} - m_Extent: {x: 250, y: 250, z: 250} - m_WorldSubdivisions: 8 - m_FrictionType: 0 - m_EnableEnhancedDeterminism: 0 - m_EnableUnifiedHeightmaps: 1 - m_DefaultMaxAngluarSpeed: 7 diff --git a/NativeWebSocket/ProjectSettings/EditorBuildSettings.asset b/NativeWebSocket/ProjectSettings/EditorBuildSettings.asset deleted file mode 100644 index 0147887..0000000 --- a/NativeWebSocket/ProjectSettings/EditorBuildSettings.asset +++ /dev/null @@ -1,8 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1045 &1 -EditorBuildSettings: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Scenes: [] - m_configObjects: {} diff --git a/NativeWebSocket/ProjectSettings/EditorSettings.asset b/NativeWebSocket/ProjectSettings/EditorSettings.asset deleted file mode 100644 index e4a3012..0000000 --- a/NativeWebSocket/ProjectSettings/EditorSettings.asset +++ /dev/null @@ -1,39 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!159 &1 -EditorSettings: - m_ObjectHideFlags: 0 - serializedVersion: 11 - m_SerializationMode: 2 - m_LineEndingsForNewScripts: 2 - m_DefaultBehaviorMode: 0 - m_PrefabRegularEnvironment: {fileID: 0} - m_PrefabUIEnvironment: {fileID: 0} - m_SpritePackerMode: 0 - m_SpritePackerPaddingPower: 1 - m_EtcTextureCompressorBehavior: 1 - m_EtcTextureFastCompressor: 1 - m_EtcTextureNormalCompressor: 2 - m_EtcTextureBestCompressor: 4 - m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref - m_ProjectGenerationRootNamespace: - m_EnableTextureStreamingInEditMode: 1 - m_EnableTextureStreamingInPlayMode: 1 - m_AsyncShaderCompilation: 1 - m_CachingShaderPreprocessor: 0 - m_EnterPlayModeOptionsEnabled: 0 - m_EnterPlayModeOptions: 3 - m_GameObjectNamingDigits: 1 - m_GameObjectNamingScheme: 0 - m_AssetNamingUsesSpace: 1 - m_UseLegacyProbeSampleCount: 1 - m_SerializeInlineMappingsOnOneLine: 0 - m_DisableCookiesInLightmapper: 1 - m_AssetPipelineMode: 1 - m_CacheServerMode: 0 - m_CacheServerEndpoint: - m_CacheServerNamespacePrefix: default - m_CacheServerEnableDownload: 1 - m_CacheServerEnableUpload: 1 - m_CacheServerEnableAuth: 0 - m_CacheServerEnableTls: 0 diff --git a/NativeWebSocket/ProjectSettings/GraphicsSettings.asset b/NativeWebSocket/ProjectSettings/GraphicsSettings.asset deleted file mode 100644 index f3874f6..0000000 --- a/NativeWebSocket/ProjectSettings/GraphicsSettings.asset +++ /dev/null @@ -1,64 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!30 &1 -GraphicsSettings: - m_ObjectHideFlags: 0 - serializedVersion: 12 - m_Deferred: - m_Mode: 1 - m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} - m_DeferredReflections: - m_Mode: 1 - m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} - m_ScreenSpaceShadows: - m_Mode: 1 - m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} - m_LegacyDeferred: - m_Mode: 1 - m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} - m_DepthNormals: - m_Mode: 1 - m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} - m_MotionVectors: - m_Mode: 1 - m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} - m_LightHalo: - m_Mode: 1 - m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} - m_LensFlare: - m_Mode: 1 - m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} - m_AlwaysIncludedShaders: - - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} - m_PreloadedShaders: [] - m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, - type: 0} - m_CustomRenderPipeline: {fileID: 0} - m_TransparencySortMode: 0 - m_TransparencySortAxis: {x: 0, y: 0, z: 1} - m_DefaultRenderingPath: 1 - m_DefaultMobileRenderingPath: 1 - m_TierSettings: [] - m_LightmapStripping: 0 - m_FogStripping: 0 - m_InstancingStripping: 0 - m_LightmapKeepPlain: 1 - m_LightmapKeepDirCombined: 1 - m_LightmapKeepDynamicPlain: 1 - m_LightmapKeepDynamicDirCombined: 1 - m_LightmapKeepShadowMask: 1 - m_LightmapKeepSubtractive: 1 - m_FogKeepLinear: 1 - m_FogKeepExp: 1 - m_FogKeepExp2: 1 - m_AlbedoSwatchInfos: [] - m_LightsUseLinearIntensity: 0 - m_LightsUseColorTemperature: 0 diff --git a/NativeWebSocket/ProjectSettings/InputManager.asset b/NativeWebSocket/ProjectSettings/InputManager.asset deleted file mode 100644 index 17c8f53..0000000 --- a/NativeWebSocket/ProjectSettings/InputManager.asset +++ /dev/null @@ -1,295 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!13 &1 -InputManager: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Axes: - - serializedVersion: 3 - m_Name: Horizontal - descriptiveName: - descriptiveNegativeName: - negativeButton: left - positiveButton: right - altNegativeButton: a - altPositiveButton: d - gravity: 3 - dead: 0.001 - sensitivity: 3 - snap: 1 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Vertical - descriptiveName: - descriptiveNegativeName: - negativeButton: down - positiveButton: up - altNegativeButton: s - altPositiveButton: w - gravity: 3 - dead: 0.001 - sensitivity: 3 - snap: 1 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Fire1 - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: left ctrl - altNegativeButton: - altPositiveButton: mouse 0 - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Fire2 - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: left alt - altNegativeButton: - altPositiveButton: mouse 1 - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Fire3 - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: left shift - altNegativeButton: - altPositiveButton: mouse 2 - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Jump - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: space - altNegativeButton: - altPositiveButton: - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Mouse X - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: - altNegativeButton: - altPositiveButton: - gravity: 0 - dead: 0 - sensitivity: 0.1 - snap: 0 - invert: 0 - type: 1 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Mouse Y - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: - altNegativeButton: - altPositiveButton: - gravity: 0 - dead: 0 - sensitivity: 0.1 - snap: 0 - invert: 0 - type: 1 - axis: 1 - joyNum: 0 - - serializedVersion: 3 - m_Name: Mouse ScrollWheel - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: - altNegativeButton: - altPositiveButton: - gravity: 0 - dead: 0 - sensitivity: 0.1 - snap: 0 - invert: 0 - type: 1 - axis: 2 - joyNum: 0 - - serializedVersion: 3 - m_Name: Horizontal - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: - altNegativeButton: - altPositiveButton: - gravity: 0 - dead: 0.19 - sensitivity: 1 - snap: 0 - invert: 0 - type: 2 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Vertical - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: - altNegativeButton: - altPositiveButton: - gravity: 0 - dead: 0.19 - sensitivity: 1 - snap: 0 - invert: 1 - type: 2 - axis: 1 - joyNum: 0 - - serializedVersion: 3 - m_Name: Fire1 - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: joystick button 0 - altNegativeButton: - altPositiveButton: - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Fire2 - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: joystick button 1 - altNegativeButton: - altPositiveButton: - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Fire3 - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: joystick button 2 - altNegativeButton: - altPositiveButton: - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Jump - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: joystick button 3 - altNegativeButton: - altPositiveButton: - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Submit - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: return - altNegativeButton: - altPositiveButton: joystick button 0 - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Submit - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: enter - altNegativeButton: - altPositiveButton: space - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 - - serializedVersion: 3 - m_Name: Cancel - descriptiveName: - descriptiveNegativeName: - negativeButton: - positiveButton: escape - altNegativeButton: - altPositiveButton: joystick button 1 - gravity: 1000 - dead: 0.001 - sensitivity: 1000 - snap: 0 - invert: 0 - type: 0 - axis: 0 - joyNum: 0 diff --git a/NativeWebSocket/ProjectSettings/NavMeshAreas.asset b/NativeWebSocket/ProjectSettings/NavMeshAreas.asset deleted file mode 100644 index ad2654e..0000000 --- a/NativeWebSocket/ProjectSettings/NavMeshAreas.asset +++ /dev/null @@ -1,93 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!126 &1 -NavMeshProjectSettings: - m_ObjectHideFlags: 0 - serializedVersion: 2 - areas: - - name: Walkable - cost: 1 - - name: Not Walkable - cost: 1 - - name: Jump - cost: 2 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - - name: - cost: 1 - m_LastAgentTypeID: -887442657 - m_Settings: - - serializedVersion: 2 - agentTypeID: 0 - agentRadius: 0.5 - agentHeight: 2 - agentSlope: 45 - agentClimb: 0.75 - ledgeDropHeight: 0 - maxJumpAcrossDistance: 0 - minRegionArea: 2 - manualCellSize: 0 - cellSize: 0.16666667 - manualTileSize: 0 - tileSize: 256 - accuratePlacement: 0 - maxJobWorkers: 0 - preserveTilesOutsideBounds: 0 - debug: - m_Flags: 0 - m_SettingNames: - - Humanoid diff --git a/NativeWebSocket/ProjectSettings/Physics2DSettings.asset b/NativeWebSocket/ProjectSettings/Physics2DSettings.asset deleted file mode 100644 index 47880b1..0000000 --- a/NativeWebSocket/ProjectSettings/Physics2DSettings.asset +++ /dev/null @@ -1,56 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!19 &1 -Physics2DSettings: - m_ObjectHideFlags: 0 - serializedVersion: 4 - m_Gravity: {x: 0, y: -9.81} - m_DefaultMaterial: {fileID: 0} - m_VelocityIterations: 8 - m_PositionIterations: 3 - m_VelocityThreshold: 1 - m_MaxLinearCorrection: 0.2 - m_MaxAngularCorrection: 8 - m_MaxTranslationSpeed: 100 - m_MaxRotationSpeed: 360 - m_BaumgarteScale: 0.2 - m_BaumgarteTimeOfImpactScale: 0.75 - m_TimeToSleep: 0.5 - m_LinearSleepTolerance: 0.01 - m_AngularSleepTolerance: 2 - m_DefaultContactOffset: 0.01 - m_JobOptions: - serializedVersion: 2 - useMultithreading: 0 - useConsistencySorting: 0 - m_InterpolationPosesPerJob: 100 - m_NewContactsPerJob: 30 - m_CollideContactsPerJob: 100 - m_ClearFlagsPerJob: 200 - m_ClearBodyForcesPerJob: 200 - m_SyncDiscreteFixturesPerJob: 50 - m_SyncContinuousFixturesPerJob: 50 - m_FindNearestContactsPerJob: 100 - m_UpdateTriggerContactsPerJob: 100 - m_IslandSolverCostThreshold: 100 - m_IslandSolverBodyCostScale: 1 - m_IslandSolverContactCostScale: 10 - m_IslandSolverJointCostScale: 10 - m_IslandSolverBodiesPerJob: 50 - m_IslandSolverContactsPerJob: 50 - m_AutoSimulation: 1 - m_QueriesHitTriggers: 1 - m_QueriesStartInColliders: 1 - m_CallbacksOnDisable: 1 - m_ReuseCollisionCallbacks: 1 - m_AutoSyncTransforms: 0 - m_AlwaysShowColliders: 0 - m_ShowColliderSleep: 1 - m_ShowColliderContacts: 0 - m_ShowColliderAABB: 0 - m_ContactArrowScale: 0.2 - m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} - m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} - m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} - m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} - m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/NativeWebSocket/ProjectSettings/PresetManager.asset b/NativeWebSocket/ProjectSettings/PresetManager.asset deleted file mode 100644 index 67a94da..0000000 --- a/NativeWebSocket/ProjectSettings/PresetManager.asset +++ /dev/null @@ -1,7 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1386491679 &1 -PresetManager: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_DefaultPresets: {} diff --git a/NativeWebSocket/ProjectSettings/ProjectSettings.asset b/NativeWebSocket/ProjectSettings/ProjectSettings.asset deleted file mode 100644 index 3f11c92..0000000 --- a/NativeWebSocket/ProjectSettings/ProjectSettings.asset +++ /dev/null @@ -1,650 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!129 &1 -PlayerSettings: - m_ObjectHideFlags: 0 - serializedVersion: 18 - productGUID: dc7225b1f8ee64a3abfd6eb883cd32ec - AndroidProfiler: 0 - AndroidFilterTouchesWhenObscured: 0 - AndroidEnableSustainedPerformanceMode: 0 - defaultScreenOrientation: 4 - targetDevice: 2 - useOnDemandResources: 0 - accelerometerFrequency: 60 - companyName: gamestdio - productName: UnityWebSockets - defaultCursor: {fileID: 0} - cursorHotspot: {x: 0, y: 0} - m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} - m_ShowUnitySplashScreen: 1 - m_ShowUnitySplashLogo: 1 - m_SplashScreenOverlayOpacity: 1 - m_SplashScreenAnimation: 1 - m_SplashScreenLogoStyle: 1 - m_SplashScreenDrawMode: 0 - m_SplashScreenBackgroundAnimationZoom: 1 - m_SplashScreenLogoAnimationZoom: 1 - m_SplashScreenBackgroundLandscapeAspect: 1 - m_SplashScreenBackgroundPortraitAspect: 1 - m_SplashScreenBackgroundLandscapeUvs: - serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 - m_SplashScreenBackgroundPortraitUvs: - serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 - m_SplashScreenLogos: [] - m_VirtualRealitySplashScreen: {fileID: 0} - m_HolographicTrackingLossScreen: {fileID: 0} - defaultScreenWidth: 1024 - defaultScreenHeight: 768 - defaultScreenWidthWeb: 960 - defaultScreenHeightWeb: 600 - m_StereoRenderingPath: 0 - m_ActiveColorSpace: 0 - m_MTRendering: 1 - m_StackTraceTypes: 010000000100000001000000010000000100000001000000 - iosShowActivityIndicatorOnLoading: -1 - androidShowActivityIndicatorOnLoading: -1 - displayResolutionDialog: 0 - iosUseCustomAppBackgroundBehavior: 0 - iosAllowHTTPDownload: 1 - allowedAutorotateToPortrait: 1 - allowedAutorotateToPortraitUpsideDown: 1 - allowedAutorotateToLandscapeRight: 1 - allowedAutorotateToLandscapeLeft: 1 - useOSAutorotation: 1 - use32BitDisplayBuffer: 1 - preserveFramebufferAlpha: 0 - disableDepthAndStencilBuffers: 0 - androidStartInFullscreen: 1 - androidRenderOutsideSafeArea: 1 - androidUseSwappy: 0 - androidBlitType: 0 - defaultIsNativeResolution: 1 - macRetinaSupport: 1 - runInBackground: 1 - captureSingleScreen: 0 - muteOtherAudioSources: 0 - Prepare IOS For Recording: 0 - Force IOS Speakers When Recording: 0 - deferSystemGesturesMode: 0 - hideHomeButton: 0 - submitAnalytics: 1 - usePlayerLog: 1 - bakeCollisionMeshes: 0 - forceSingleInstance: 0 - useFlipModelSwapchain: 1 - resizableWindow: 0 - useMacAppStoreValidation: 0 - macAppStoreCategory: public.app-category.games - gpuSkinning: 1 - graphicsJobs: 0 - xboxPIXTextureCapture: 0 - xboxEnableAvatar: 0 - xboxEnableKinect: 0 - xboxEnableKinectAutoTracking: 0 - xboxEnableFitness: 0 - visibleInBackground: 1 - allowFullscreenSwitch: 1 - graphicsJobMode: 0 - fullscreenMode: 1 - xboxSpeechDB: 0 - xboxEnableHeadOrientation: 0 - xboxEnableGuest: 0 - xboxEnablePIXSampling: 0 - metalFramebufferOnly: 0 - xboxOneResolution: 0 - xboxOneSResolution: 0 - xboxOneXResolution: 3 - xboxOneMonoLoggingLevel: 0 - xboxOneLoggingLevel: 1 - xboxOneDisableEsram: 0 - xboxOnePresentImmediateThreshold: 0 - switchQueueCommandMemory: 0 - switchQueueControlMemory: 16384 - switchQueueComputeMemory: 262144 - switchNVNShaderPoolsGranularity: 33554432 - switchNVNDefaultPoolsGranularity: 16777216 - switchNVNOtherPoolsGranularity: 16777216 - vulkanEnableSetSRGBWrite: 0 - m_SupportedAspectRatios: - 4:3: 1 - 5:4: 1 - 16:10: 1 - 16:9: 1 - Others: 1 - bundleVersion: 1.0 - preloadedAssets: [] - metroInputSource: 0 - wsaTransparentSwapchain: 0 - m_HolographicPauseOnTrackingLoss: 1 - xboxOneDisableKinectGpuReservation: 1 - xboxOneEnable7thCore: 1 - vrSettings: - cardboard: - depthFormat: 0 - enableTransitionView: 0 - daydream: - depthFormat: 0 - useSustainedPerformanceMode: 0 - enableVideoLayer: 0 - useProtectedVideoMemory: 0 - minimumSupportedHeadTracking: 0 - maximumSupportedHeadTracking: 1 - hololens: - depthFormat: 1 - depthBufferSharingEnabled: 1 - lumin: - depthFormat: 0 - frameTiming: 2 - enableGLCache: 0 - glCacheMaxBlobSize: 524288 - glCacheMaxFileSize: 8388608 - oculus: - sharedDepthBuffer: 1 - dashSupport: 1 - lowOverheadMode: 0 - enable360StereoCapture: 0 - isWsaHolographicRemotingEnabled: 0 - protectGraphicsMemory: 0 - enableFrameTimingStats: 0 - useHDRDisplay: 0 - m_ColorGamuts: 00000000 - targetPixelDensity: 30 - resolutionScalingMode: 0 - androidSupportedAspectRatio: 1 - androidMaxAspectRatio: 2.1 - applicationIdentifier: {} - buildNumber: {} - AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 16 - AndroidTargetSdkVersion: 0 - AndroidPreferredInstallLocation: 1 - aotOptions: - stripEngineCode: 1 - iPhoneStrippingLevel: 0 - iPhoneScriptCallOptimization: 0 - ForceInternetPermission: 0 - ForceSDCardPermission: 0 - CreateWallpaper: 0 - APKExpansionFiles: 0 - keepLoadedShadersAlive: 0 - StripUnusedMeshComponents: 1 - VertexChannelCompressionMask: 4054 - iPhoneSdkVersion: 988 - iOSTargetOSVersionString: 9.0 - tvOSSdkVersion: 0 - tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 9.0 - uIPrerenderedIcon: 0 - uIRequiresPersistentWiFi: 0 - uIRequiresFullScreen: 1 - uIStatusBarHidden: 1 - uIExitOnSuspend: 0 - uIStatusBarStyle: 0 - iPhoneSplashScreen: {fileID: 0} - iPhoneHighResSplashScreen: {fileID: 0} - iPhoneTallHighResSplashScreen: {fileID: 0} - iPhone47inSplashScreen: {fileID: 0} - iPhone55inPortraitSplashScreen: {fileID: 0} - iPhone55inLandscapeSplashScreen: {fileID: 0} - iPhone58inPortraitSplashScreen: {fileID: 0} - iPhone58inLandscapeSplashScreen: {fileID: 0} - iPadPortraitSplashScreen: {fileID: 0} - iPadHighResPortraitSplashScreen: {fileID: 0} - iPadLandscapeSplashScreen: {fileID: 0} - iPadHighResLandscapeSplashScreen: {fileID: 0} - iPhone65inPortraitSplashScreen: {fileID: 0} - iPhone65inLandscapeSplashScreen: {fileID: 0} - iPhone61inPortraitSplashScreen: {fileID: 0} - iPhone61inLandscapeSplashScreen: {fileID: 0} - appleTVSplashScreen: {fileID: 0} - appleTVSplashScreen2x: {fileID: 0} - tvOSSmallIconLayers: [] - tvOSSmallIconLayers2x: [] - tvOSLargeIconLayers: [] - tvOSLargeIconLayers2x: [] - tvOSTopShelfImageLayers: [] - tvOSTopShelfImageLayers2x: [] - tvOSTopShelfImageWideLayers: [] - tvOSTopShelfImageWideLayers2x: [] - iOSLaunchScreenType: 0 - iOSLaunchScreenPortrait: {fileID: 0} - iOSLaunchScreenLandscape: {fileID: 0} - iOSLaunchScreenBackgroundColor: - serializedVersion: 2 - rgba: 0 - iOSLaunchScreenFillPct: 100 - iOSLaunchScreenSize: 100 - iOSLaunchScreenCustomXibPath: - iOSLaunchScreeniPadType: 0 - iOSLaunchScreeniPadImage: {fileID: 0} - iOSLaunchScreeniPadBackgroundColor: - serializedVersion: 2 - rgba: 0 - iOSLaunchScreeniPadFillPct: 100 - iOSLaunchScreeniPadSize: 100 - iOSLaunchScreeniPadCustomXibPath: - iOSUseLaunchScreenStoryboard: 0 - iOSLaunchScreenCustomStoryboardPath: - iOSDeviceRequirements: [] - iOSURLSchemes: [] - iOSBackgroundModes: 0 - iOSMetalForceHardShadows: 0 - metalEditorSupport: 1 - metalAPIValidation: 1 - iOSRenderExtraFrameOnPause: 0 - appleDeveloperTeamID: - iOSManualSigningProvisioningProfileID: - tvOSManualSigningProvisioningProfileID: - iOSManualSigningProvisioningProfileType: 0 - tvOSManualSigningProvisioningProfileType: 0 - appleEnableAutomaticSigning: 0 - iOSRequireARKit: 0 - iOSAutomaticallyDetectAndAddCapabilities: 1 - appleEnableProMotion: 0 - clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea - templatePackageId: com.unity.template.3d@3.1.0 - templateDefaultScene: Assets/Scenes/SampleScene.unity - AndroidTargetArchitectures: 1 - AndroidSplashScreenScale: 0 - androidSplashScreen: {fileID: 0} - AndroidKeystoreName: '{inproject}: ' - AndroidKeyaliasName: - AndroidBuildApkPerCpuArchitecture: 0 - AndroidTVCompatibility: 0 - AndroidIsGame: 1 - AndroidEnableTango: 0 - androidEnableBanner: 1 - androidUseLowAccuracyLocation: 0 - androidUseCustomKeystore: 0 - m_AndroidBanners: - - width: 320 - height: 180 - banner: {fileID: 0} - androidGamepadSupportLevel: 0 - AndroidValidateAppBundleSize: 1 - AndroidAppBundleSizeToValidate: 150 - resolutionDialogBanner: {fileID: 0} - m_BuildTargetIcons: [] - m_BuildTargetPlatformIcons: [] - m_BuildTargetBatching: - - m_BuildTarget: Standalone - m_StaticBatching: 1 - m_DynamicBatching: 0 - - m_BuildTarget: tvOS - m_StaticBatching: 1 - m_DynamicBatching: 0 - - m_BuildTarget: Android - m_StaticBatching: 1 - m_DynamicBatching: 0 - - m_BuildTarget: iPhone - m_StaticBatching: 1 - m_DynamicBatching: 0 - - m_BuildTarget: WebGL - m_StaticBatching: 0 - m_DynamicBatching: 0 - m_BuildTargetGraphicsAPIs: - - m_BuildTarget: AndroidPlayer - m_APIs: 150000000b000000 - m_Automatic: 0 - - m_BuildTarget: iOSSupport - m_APIs: 10000000 - m_Automatic: 1 - - m_BuildTarget: AppleTVSupport - m_APIs: 10000000 - m_Automatic: 0 - - m_BuildTarget: WebGLSupport - m_APIs: 0b000000 - m_Automatic: 1 - m_BuildTargetVRSettings: - - m_BuildTarget: Standalone - m_Enabled: 0 - m_Devices: - - Oculus - - OpenVR - openGLRequireES31: 0 - openGLRequireES31AEP: 0 - openGLRequireES32: 0 - vuforiaEnabled: 0 - m_TemplateCustomTags: {} - mobileMTRendering: - Android: 1 - iPhone: 1 - tvOS: 1 - m_BuildTargetGroupLightmapEncodingQuality: [] - m_BuildTargetGroupLightmapSettings: [] - playModeTestRunnerEnabled: 0 - runPlayModeTestAsEditModeTest: 0 - actionOnDotNetUnhandledException: 1 - enableInternalProfiler: 0 - logObjCUncaughtExceptions: 1 - enableCrashReportAPI: 0 - cameraUsageDescription: - locationUsageDescription: - microphoneUsageDescription: - switchNetLibKey: - switchSocketMemoryPoolSize: 6144 - switchSocketAllocatorPoolSize: 128 - switchSocketConcurrencyLimit: 14 - switchScreenResolutionBehavior: 2 - switchUseCPUProfiler: 0 - switchApplicationID: 0x01004b9000490000 - switchNSODependencies: - switchTitleNames_0: - switchTitleNames_1: - switchTitleNames_2: - switchTitleNames_3: - switchTitleNames_4: - switchTitleNames_5: - switchTitleNames_6: - switchTitleNames_7: - switchTitleNames_8: - switchTitleNames_9: - switchTitleNames_10: - switchTitleNames_11: - switchTitleNames_12: - switchTitleNames_13: - switchTitleNames_14: - switchPublisherNames_0: - switchPublisherNames_1: - switchPublisherNames_2: - switchPublisherNames_3: - switchPublisherNames_4: - switchPublisherNames_5: - switchPublisherNames_6: - switchPublisherNames_7: - switchPublisherNames_8: - switchPublisherNames_9: - switchPublisherNames_10: - switchPublisherNames_11: - switchPublisherNames_12: - switchPublisherNames_13: - switchPublisherNames_14: - switchIcons_0: {fileID: 0} - switchIcons_1: {fileID: 0} - switchIcons_2: {fileID: 0} - switchIcons_3: {fileID: 0} - switchIcons_4: {fileID: 0} - switchIcons_5: {fileID: 0} - switchIcons_6: {fileID: 0} - switchIcons_7: {fileID: 0} - switchIcons_8: {fileID: 0} - switchIcons_9: {fileID: 0} - switchIcons_10: {fileID: 0} - switchIcons_11: {fileID: 0} - switchIcons_12: {fileID: 0} - switchIcons_13: {fileID: 0} - switchIcons_14: {fileID: 0} - switchSmallIcons_0: {fileID: 0} - switchSmallIcons_1: {fileID: 0} - switchSmallIcons_2: {fileID: 0} - switchSmallIcons_3: {fileID: 0} - switchSmallIcons_4: {fileID: 0} - switchSmallIcons_5: {fileID: 0} - switchSmallIcons_6: {fileID: 0} - switchSmallIcons_7: {fileID: 0} - switchSmallIcons_8: {fileID: 0} - switchSmallIcons_9: {fileID: 0} - switchSmallIcons_10: {fileID: 0} - switchSmallIcons_11: {fileID: 0} - switchSmallIcons_12: {fileID: 0} - switchSmallIcons_13: {fileID: 0} - switchSmallIcons_14: {fileID: 0} - switchManualHTML: - switchAccessibleURLs: - switchLegalInformation: - switchMainThreadStackSize: 1048576 - switchPresenceGroupId: - switchLogoHandling: 0 - switchReleaseVersion: 0 - switchDisplayVersion: 1.0.0 - switchStartupUserAccount: 0 - switchTouchScreenUsage: 0 - switchSupportedLanguagesMask: 0 - switchLogoType: 0 - switchApplicationErrorCodeCategory: - switchUserAccountSaveDataSize: 0 - switchUserAccountSaveDataJournalSize: 0 - switchApplicationAttribute: 0 - switchCardSpecSize: -1 - switchCardSpecClock: -1 - switchRatingsMask: 0 - switchRatingsInt_0: 0 - switchRatingsInt_1: 0 - switchRatingsInt_2: 0 - switchRatingsInt_3: 0 - switchRatingsInt_4: 0 - switchRatingsInt_5: 0 - switchRatingsInt_6: 0 - switchRatingsInt_7: 0 - switchRatingsInt_8: 0 - switchRatingsInt_9: 0 - switchRatingsInt_10: 0 - switchRatingsInt_11: 0 - switchLocalCommunicationIds_0: - switchLocalCommunicationIds_1: - switchLocalCommunicationIds_2: - switchLocalCommunicationIds_3: - switchLocalCommunicationIds_4: - switchLocalCommunicationIds_5: - switchLocalCommunicationIds_6: - switchLocalCommunicationIds_7: - switchParentalControl: 0 - switchAllowsScreenshot: 1 - switchAllowsVideoCapturing: 1 - switchAllowsRuntimeAddOnContentInstall: 0 - switchDataLossConfirmation: 0 - switchUserAccountLockEnabled: 0 - switchSystemResourceMemory: 16777216 - switchSupportedNpadStyles: 3 - switchNativeFsCacheSize: 32 - switchIsHoldTypeHorizontal: 0 - switchSupportedNpadCount: 8 - switchSocketConfigEnabled: 0 - switchTcpInitialSendBufferSize: 32 - switchTcpInitialReceiveBufferSize: 64 - switchTcpAutoSendBufferSizeMax: 256 - switchTcpAutoReceiveBufferSizeMax: 256 - switchUdpSendBufferSize: 9 - switchUdpReceiveBufferSize: 42 - switchSocketBufferEfficiency: 4 - switchSocketInitializeEnabled: 1 - switchNetworkInterfaceManagerInitializeEnabled: 1 - switchPlayerConnectionEnabled: 1 - ps4NPAgeRating: 12 - ps4NPTitleSecret: - ps4NPTrophyPackPath: - ps4ParentalLevel: 11 - ps4ContentID: ED1633-NPXX51362_00-0000000000000000 - ps4Category: 0 - ps4MasterVersion: 01.00 - ps4AppVersion: 01.00 - ps4AppType: 0 - ps4ParamSfxPath: - ps4VideoOutPixelFormat: 0 - ps4VideoOutInitialWidth: 1920 - ps4VideoOutBaseModeInitialWidth: 1920 - ps4VideoOutReprojectionRate: 60 - ps4PronunciationXMLPath: - ps4PronunciationSIGPath: - ps4BackgroundImagePath: - ps4StartupImagePath: - ps4StartupImagesFolder: - ps4IconImagesFolder: - ps4SaveDataImagePath: - ps4SdkOverride: - ps4BGMPath: - ps4ShareFilePath: - ps4ShareOverlayImagePath: - ps4PrivacyGuardImagePath: - ps4NPtitleDatPath: - ps4RemotePlayKeyAssignment: -1 - ps4RemotePlayKeyMappingDir: - ps4PlayTogetherPlayerCount: 0 - ps4EnterButtonAssignment: 1 - ps4ApplicationParam1: 0 - ps4ApplicationParam2: 0 - ps4ApplicationParam3: 0 - ps4ApplicationParam4: 0 - ps4DownloadDataSize: 0 - ps4GarlicHeapSize: 2048 - ps4ProGarlicHeapSize: 2560 - playerPrefsMaxSize: 32768 - ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ - ps4pnSessions: 1 - ps4pnPresence: 1 - ps4pnFriends: 1 - ps4pnGameCustomData: 1 - playerPrefsSupport: 0 - enableApplicationExit: 0 - resetTempFolder: 1 - restrictedAudioUsageRights: 0 - ps4UseResolutionFallback: 0 - ps4ReprojectionSupport: 0 - ps4UseAudio3dBackend: 0 - ps4SocialScreenEnabled: 0 - ps4ScriptOptimizationLevel: 0 - ps4Audio3dVirtualSpeakerCount: 14 - ps4attribCpuUsage: 0 - ps4PatchPkgPath: - ps4PatchLatestPkgPath: - ps4PatchChangeinfoPath: - ps4PatchDayOne: 0 - ps4attribUserManagement: 0 - ps4attribMoveSupport: 0 - ps4attrib3DSupport: 0 - ps4attribShareSupport: 0 - ps4attribExclusiveVR: 0 - ps4disableAutoHideSplash: 0 - ps4videoRecordingFeaturesUsed: 0 - ps4contentSearchFeaturesUsed: 0 - ps4attribEyeToEyeDistanceSettingVR: 0 - ps4IncludedModules: [] - monoEnv: - splashScreenBackgroundSourceLandscape: {fileID: 0} - splashScreenBackgroundSourcePortrait: {fileID: 0} - blurSplashScreenBackground: 1 - spritePackerPolicy: - webGLMemorySize: 16 - webGLExceptionSupport: 1 - webGLNameFilesAsHashes: 0 - webGLDataCaching: 1 - webGLDebugSymbols: 0 - webGLEmscriptenArgs: - webGLModulesDirectory: - webGLTemplate: APPLICATION:Default - webGLAnalyzeBuildSize: 0 - webGLUseEmbeddedResources: 0 - webGLCompressionFormat: 1 - webGLLinkerTarget: 1 - webGLThreadsSupport: 0 - webGLWasmStreaming: 0 - scriptingDefineSymbols: {} - platformArchitecture: {} - scriptingBackend: {} - il2cppCompilerConfiguration: {} - managedStrippingLevel: {} - incrementalIl2cppBuild: {} - allowUnsafeCode: 0 - additionalIl2CppArgs: - scriptingRuntimeVersion: 1 - gcIncremental: 0 - gcWBarrierValidation: 0 - apiCompatibilityLevelPerPlatform: - WebGL: 3 - m_RenderingPath: 1 - m_MobileRenderingPath: 1 - metroPackageName: Template_3D - metroPackageVersion: - metroCertificatePath: - metroCertificatePassword: - metroCertificateSubject: - metroCertificateIssuer: - metroCertificateNotAfter: 0000000000000000 - metroApplicationDescription: Template_3D - wsaImages: {} - metroTileShortName: - metroTileShowName: 0 - metroMediumTileShowName: 0 - metroLargeTileShowName: 0 - metroWideTileShowName: 0 - metroSupportStreamingInstall: 0 - metroLastRequiredScene: 0 - metroDefaultTileSize: 1 - metroTileForegroundText: 2 - metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} - metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, - a: 1} - metroSplashScreenUseBackgroundColor: 0 - platformCapabilities: {} - metroTargetDeviceFamilies: {} - metroFTAName: - metroFTAFileTypes: [] - metroProtocolName: - XboxOneProductId: - XboxOneUpdateKey: - XboxOneSandboxId: - XboxOneContentId: - XboxOneTitleId: - XboxOneSCId: - XboxOneGameOsOverridePath: - XboxOnePackagingOverridePath: - XboxOneAppManifestOverridePath: - XboxOneVersion: 1.0.0.0 - XboxOnePackageEncryption: 0 - XboxOnePackageUpdateGranularity: 2 - XboxOneDescription: - XboxOneLanguage: - - enus - XboxOneCapability: [] - XboxOneGameRating: {} - XboxOneIsContentPackage: 0 - XboxOneEnableGPUVariability: 1 - XboxOneSockets: {} - XboxOneSplashScreen: {fileID: 0} - XboxOneAllowedProductIds: [] - XboxOnePersistentLocalStorageSize: 0 - XboxOneXTitleMemory: 8 - xboxOneScriptCompiler: 1 - XboxOneOverrideIdentityName: - vrEditorSettings: - daydream: - daydreamIconForeground: {fileID: 0} - daydreamIconBackground: {fileID: 0} - cloudServicesEnabled: - UNet: 1 - luminIcon: - m_Name: - m_ModelFolderPath: - m_PortalFolderPath: - luminCert: - m_CertPath: - m_SignPackage: 1 - luminIsChannelApp: 0 - luminVersion: - m_VersionCode: 1 - m_VersionName: - facebookSdkVersion: 7.9.4 - facebookAppId: - facebookCookies: 1 - facebookLogging: 1 - facebookStatus: 1 - facebookXfbml: 0 - facebookFrictionlessRequests: 1 - apiCompatibilityLevel: 6 - cloudProjectId: - framebufferDepthMemorylessMode: 0 - projectName: - organizationId: - cloudEnabled: 0 - enableNativePlatformBackendsForNewInputSystem: 0 - disableOldInputManagerSupport: 0 - legacyClampBlendShapeWeights: 1 diff --git a/NativeWebSocket/ProjectSettings/ProjectVersion.txt b/NativeWebSocket/ProjectSettings/ProjectVersion.txt deleted file mode 100644 index a4b5984..0000000 --- a/NativeWebSocket/ProjectSettings/ProjectVersion.txt +++ /dev/null @@ -1,2 +0,0 @@ -m_EditorVersion: 6000.3.6f1 -m_EditorVersionWithRevision: 6000.3.6f1 (bbb010bdb8a3) diff --git a/NativeWebSocket/ProjectSettings/QualitySettings.asset b/NativeWebSocket/ProjectSettings/QualitySettings.asset deleted file mode 100644 index ed26313..0000000 --- a/NativeWebSocket/ProjectSettings/QualitySettings.asset +++ /dev/null @@ -1,231 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!47 &1 -QualitySettings: - m_ObjectHideFlags: 0 - serializedVersion: 5 - m_CurrentQuality: 5 - m_QualitySettings: - - serializedVersion: 2 - name: Very Low - pixelLightCount: 0 - shadows: 0 - shadowResolution: 0 - shadowProjection: 1 - shadowCascades: 1 - shadowDistance: 15 - shadowNearPlaneOffset: 3 - shadowCascade2Split: 0.33333334 - shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 0 - blendWeights: 1 - textureQuality: 1 - anisotropicTextures: 0 - antiAliasing: 0 - softParticles: 0 - softVegetation: 0 - realtimeReflectionProbes: 0 - billboardsFaceCameraPosition: 0 - vSyncCount: 0 - lodBias: 0.3 - maximumLODLevel: 0 - streamingMipmapsActive: 0 - streamingMipmapsAddAllCameras: 1 - streamingMipmapsMemoryBudget: 512 - streamingMipmapsRenderersPerFrame: 512 - streamingMipmapsMaxLevelReduction: 2 - streamingMipmapsMaxFileIORequests: 1024 - particleRaycastBudget: 4 - asyncUploadTimeSlice: 2 - asyncUploadBufferSize: 16 - asyncUploadPersistentBuffer: 1 - resolutionScalingFixedDPIFactor: 1 - excludedTargetPlatforms: [] - - serializedVersion: 2 - name: Low - pixelLightCount: 0 - shadows: 0 - shadowResolution: 0 - shadowProjection: 1 - shadowCascades: 1 - shadowDistance: 20 - shadowNearPlaneOffset: 3 - shadowCascade2Split: 0.33333334 - shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 0 - blendWeights: 2 - textureQuality: 0 - anisotropicTextures: 0 - antiAliasing: 0 - softParticles: 0 - softVegetation: 0 - realtimeReflectionProbes: 0 - billboardsFaceCameraPosition: 0 - vSyncCount: 0 - lodBias: 0.4 - maximumLODLevel: 0 - streamingMipmapsActive: 0 - streamingMipmapsAddAllCameras: 1 - streamingMipmapsMemoryBudget: 512 - streamingMipmapsRenderersPerFrame: 512 - streamingMipmapsMaxLevelReduction: 2 - streamingMipmapsMaxFileIORequests: 1024 - particleRaycastBudget: 16 - asyncUploadTimeSlice: 2 - asyncUploadBufferSize: 16 - asyncUploadPersistentBuffer: 1 - resolutionScalingFixedDPIFactor: 1 - excludedTargetPlatforms: [] - - serializedVersion: 2 - name: Medium - pixelLightCount: 1 - shadows: 1 - shadowResolution: 0 - shadowProjection: 1 - shadowCascades: 1 - shadowDistance: 20 - shadowNearPlaneOffset: 3 - shadowCascade2Split: 0.33333334 - shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 0 - blendWeights: 2 - textureQuality: 0 - anisotropicTextures: 1 - antiAliasing: 0 - softParticles: 0 - softVegetation: 0 - realtimeReflectionProbes: 0 - billboardsFaceCameraPosition: 0 - vSyncCount: 1 - lodBias: 0.7 - maximumLODLevel: 0 - streamingMipmapsActive: 0 - streamingMipmapsAddAllCameras: 1 - streamingMipmapsMemoryBudget: 512 - streamingMipmapsRenderersPerFrame: 512 - streamingMipmapsMaxLevelReduction: 2 - streamingMipmapsMaxFileIORequests: 1024 - particleRaycastBudget: 64 - asyncUploadTimeSlice: 2 - asyncUploadBufferSize: 16 - asyncUploadPersistentBuffer: 1 - resolutionScalingFixedDPIFactor: 1 - excludedTargetPlatforms: [] - - serializedVersion: 2 - name: High - pixelLightCount: 2 - shadows: 2 - shadowResolution: 1 - shadowProjection: 1 - shadowCascades: 2 - shadowDistance: 40 - shadowNearPlaneOffset: 3 - shadowCascade2Split: 0.33333334 - shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 1 - blendWeights: 2 - textureQuality: 0 - anisotropicTextures: 1 - antiAliasing: 0 - softParticles: 0 - softVegetation: 1 - realtimeReflectionProbes: 1 - billboardsFaceCameraPosition: 1 - vSyncCount: 1 - lodBias: 1 - maximumLODLevel: 0 - streamingMipmapsActive: 0 - streamingMipmapsAddAllCameras: 1 - streamingMipmapsMemoryBudget: 512 - streamingMipmapsRenderersPerFrame: 512 - streamingMipmapsMaxLevelReduction: 2 - streamingMipmapsMaxFileIORequests: 1024 - particleRaycastBudget: 256 - asyncUploadTimeSlice: 2 - asyncUploadBufferSize: 16 - asyncUploadPersistentBuffer: 1 - resolutionScalingFixedDPIFactor: 1 - excludedTargetPlatforms: [] - - serializedVersion: 2 - name: Very High - pixelLightCount: 3 - shadows: 2 - shadowResolution: 2 - shadowProjection: 1 - shadowCascades: 2 - shadowDistance: 70 - shadowNearPlaneOffset: 3 - shadowCascade2Split: 0.33333334 - shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 1 - blendWeights: 4 - textureQuality: 0 - anisotropicTextures: 2 - antiAliasing: 2 - softParticles: 1 - softVegetation: 1 - realtimeReflectionProbes: 1 - billboardsFaceCameraPosition: 1 - vSyncCount: 1 - lodBias: 1.5 - maximumLODLevel: 0 - streamingMipmapsActive: 0 - streamingMipmapsAddAllCameras: 1 - streamingMipmapsMemoryBudget: 512 - streamingMipmapsRenderersPerFrame: 512 - streamingMipmapsMaxLevelReduction: 2 - streamingMipmapsMaxFileIORequests: 1024 - particleRaycastBudget: 1024 - asyncUploadTimeSlice: 2 - asyncUploadBufferSize: 16 - asyncUploadPersistentBuffer: 1 - resolutionScalingFixedDPIFactor: 1 - excludedTargetPlatforms: [] - - serializedVersion: 2 - name: Ultra - pixelLightCount: 4 - shadows: 2 - shadowResolution: 2 - shadowProjection: 1 - shadowCascades: 4 - shadowDistance: 150 - shadowNearPlaneOffset: 3 - shadowCascade2Split: 0.33333334 - shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 1 - blendWeights: 4 - textureQuality: 0 - anisotropicTextures: 2 - antiAliasing: 2 - softParticles: 1 - softVegetation: 1 - realtimeReflectionProbes: 1 - billboardsFaceCameraPosition: 1 - vSyncCount: 1 - lodBias: 2 - maximumLODLevel: 0 - streamingMipmapsActive: 0 - streamingMipmapsAddAllCameras: 1 - streamingMipmapsMemoryBudget: 512 - streamingMipmapsRenderersPerFrame: 512 - streamingMipmapsMaxLevelReduction: 2 - streamingMipmapsMaxFileIORequests: 1024 - particleRaycastBudget: 4096 - asyncUploadTimeSlice: 2 - asyncUploadBufferSize: 16 - asyncUploadPersistentBuffer: 1 - resolutionScalingFixedDPIFactor: 1 - excludedTargetPlatforms: [] - m_PerPlatformDefaultQuality: - Android: 2 - Lumin: 5 - Nintendo 3DS: 5 - Nintendo Switch: 5 - PS4: 5 - PSP2: 2 - Standalone: 5 - WebGL: 3 - Windows Store Apps: 5 - XboxOne: 5 - iPhone: 2 - tvOS: 2 diff --git a/NativeWebSocket/ProjectSettings/TagManager.asset b/NativeWebSocket/ProjectSettings/TagManager.asset deleted file mode 100644 index 1c92a78..0000000 --- a/NativeWebSocket/ProjectSettings/TagManager.asset +++ /dev/null @@ -1,43 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!78 &1 -TagManager: - serializedVersion: 2 - tags: [] - layers: - - Default - - TransparentFX - - Ignore Raycast - - - - Water - - UI - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - m_SortingLayers: - - name: Default - uniqueID: 0 - locked: 0 diff --git a/NativeWebSocket/ProjectSettings/TimeManager.asset b/NativeWebSocket/ProjectSettings/TimeManager.asset deleted file mode 100644 index 558a017..0000000 --- a/NativeWebSocket/ProjectSettings/TimeManager.asset +++ /dev/null @@ -1,9 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!5 &1 -TimeManager: - m_ObjectHideFlags: 0 - Fixed Timestep: 0.02 - Maximum Allowed Timestep: 0.33333334 - m_TimeScale: 1 - Maximum Particle Timestep: 0.03 diff --git a/NativeWebSocket/ProjectSettings/UnityConnectSettings.asset b/NativeWebSocket/ProjectSettings/UnityConnectSettings.asset deleted file mode 100644 index fa0b146..0000000 --- a/NativeWebSocket/ProjectSettings/UnityConnectSettings.asset +++ /dev/null @@ -1,34 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!310 &1 -UnityConnectSettings: - m_ObjectHideFlags: 0 - serializedVersion: 1 - m_Enabled: 0 - m_TestMode: 0 - m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events - m_EventUrl: https://cdp.cloud.unity3d.com/v1/events - m_ConfigUrl: https://config.uca.cloud.unity3d.com - m_TestInitMode: 0 - CrashReportingSettings: - m_EventUrl: https://perf-events.cloud.unity3d.com - m_Enabled: 0 - m_LogBufferSize: 10 - m_CaptureEditorExceptions: 1 - UnityPurchasingSettings: - m_Enabled: 0 - m_TestMode: 0 - UnityAnalyticsSettings: - m_Enabled: 0 - m_TestMode: 0 - m_InitializeOnStartup: 1 - UnityAdsSettings: - m_Enabled: 0 - m_InitializeOnStartup: 1 - m_TestMode: 0 - m_IosGameId: - m_AndroidGameId: - m_GameIds: {} - m_GameId: - PerformanceReportingSettings: - m_Enabled: 0 diff --git a/NativeWebSocket/ProjectSettings/VFXManager.asset b/NativeWebSocket/ProjectSettings/VFXManager.asset deleted file mode 100644 index 46f38e1..0000000 --- a/NativeWebSocket/ProjectSettings/VFXManager.asset +++ /dev/null @@ -1,14 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!937362698 &1 -VFXManager: - m_ObjectHideFlags: 0 - m_IndirectShader: {fileID: 0} - m_CopyBufferShader: {fileID: 0} - m_SortShader: {fileID: 0} - m_StripUpdateShader: {fileID: 0} - m_RenderPipeSettingsPath: - m_FixedTimeStep: 0.016666668 - m_MaxDeltaTime: 0.05 - m_CompiledVersion: 0 - m_RuntimeVersion: 0 diff --git a/NativeWebSocket/ProjectSettings/XRSettings.asset b/NativeWebSocket/ProjectSettings/XRSettings.asset deleted file mode 100644 index 482590c..0000000 --- a/NativeWebSocket/ProjectSettings/XRSettings.asset +++ /dev/null @@ -1,10 +0,0 @@ -{ - "m_SettingKeys": [ - "VR Device Disabled", - "VR Device User Alert" - ], - "m_SettingValues": [ - "False", - "False" - ] -} \ No newline at end of file diff --git a/NodeServer/package-lock.json b/NodeServer/package-lock.json deleted file mode 100644 index 37226fd..0000000 --- a/NodeServer/package-lock.json +++ /dev/null @@ -1,889 +0,0 @@ -{ - "name": "nodeserver", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "nodeserver", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "express": "^4.17.1", - "ws": "^7.1.2" - }, - "engines": { - "node": "12.x" - } - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dependencies": { - "mime-db": "1.40.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ws": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", - "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", - "dependencies": { - "async-limiter": "^1.0.0" - } - } - }, - "dependencies": { - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "ws": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", - "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", - "requires": { - "async-limiter": "^1.0.0" - } - } - } -} diff --git a/README.md b/README.md index f04440d..6e96aaa 100644 --- a/README.md +++ b/README.md @@ -1,121 +1,267 @@ Native WebSocket -This is the simplest and easiest WebSocket library for Unity you can find! +A simple, dependency-free WebSocket client library for **Unity**, **MonoGame**, **Godot**, and any **.NET** project. - No external DLL's required (uses built-in `System.Net.WebSockets`) -- WebGL/HTML5 support +- WebGL/HTML5 support (Unity) - Supports all major build targets +- Automatic main-thread event dispatching via `SynchronizationContext` - Very simple API -- (Used in [Colyseus Unity SDK](https://github.com/colyseus/colyseus-unity-sdk)) -### Consider supporting my work on [Patreon](https://patreon.com/endel) | [Ko-fi](https://ko-fi.com/endeld) | [PayPal](https://www.paypal.me/endeld) +Used in [Colyseus Unity SDK](https://github.com/colyseus/colyseus-unity-sdk). -[![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dendel%26type%3Dpatrons&style=for-the-badge)](https://patreon.com/endel) +# Installation -## Installation +## Unity *Requires Unity 2019.1+ with .NET 4.x+ Runtime* -### Install via UPM (Unity Package Manager) +> **Note:** Do not copy the raw source files from this repository directly into +> your Unity project. The core `WebSocket.cs` requires a build-time +> transformation to add WebGL conditional compilation guards. Use one of the +> install methods below instead. + +**Via UPM (Unity Package Manager):** 1. Open Unity 2. Open Package Manager Window 3. Click Add Package From Git URL -4. Enter URL: ```https://github.com/endel/NativeWebSocket.git#upm``` +4. Enter URL: `https://github.com/endel/NativeWebSocket.git#upm-2.0` + +**Via .unitypackage:** +1. Download `NativeWebSocket.unitypackage` from the [Releases](https://github.com/endel/NativeWebSocket/releases) page +2. In Unity, go to Assets > Import Package > Custom Package and select the downloaded file + +## MonoGame / .NET + +```bash +dotnet add package Colyseus.NativeWebSocket +dotnet add package Colyseus.NativeWebSocket.MonoGame +``` -### Install manually -1. [Download this project](https://github.com/endel/NativeWebSocket/archive/master.zip) -2. Copy the sources from `NativeWebSocket/Assets/WebSocket` into your `Assets` directory. +## Godot (C#) -## Usage +```bash +dotnet add package Colyseus.NativeWebSocket +``` + +# Usage + +## Unity ```csharp -using System; -using System.Collections; -using System.Collections.Generic; using UnityEngine; - using NativeWebSocket; public class Connection : MonoBehaviour { - WebSocket websocket; + WebSocket websocket; - // Start is called before the first frame update - async void Start() - { - websocket = new WebSocket("ws://localhost:3000"); - - websocket.OnOpen += () => + async void Start() { - Debug.Log("Connection open!"); - }; + Application.runInBackground = true; // Recommended for WebGL + + websocket = new WebSocket("ws://localhost:3000"); + + websocket.OnOpen += () => Debug.Log("Connection open!"); + websocket.OnError += (e) => Debug.Log("Error! " + e); + websocket.OnClose += (code) => Debug.Log("Connection closed!"); + + websocket.OnMessage += (bytes) => + { + var message = System.Text.Encoding.UTF8.GetString(bytes); + Debug.Log("Received: " + message); + }; + + InvokeRepeating("SendWebSocketMessage", 0.0f, 0.3f); - websocket.OnError += (e) => + await websocket.Connect(); + } + + async void SendWebSocketMessage() { - Debug.Log("Error! " + e); - }; + if (websocket.State == WebSocketState.Open) + { + await websocket.Send(new byte[] { 10, 20, 30 }); + await websocket.SendText("plain text message"); + } + } - websocket.OnClose += (e) => + private async void OnApplicationQuit() { - Debug.Log("Connection closed!"); - }; + await websocket.Close(); + } +} +``` + +**WebGL note:** Unity pauses the game loop when the browser tab loses focus, which +stops all WebSocket send/receive callbacks. To keep the connection active in the +background, set `Application.runInBackground = true` in your script or enable +**Run In Background** in Player Settings > Resolution and Presentation. + +## MonoGame + +Add the `WebSocketGameComponent` to your game. This installs a +`SynchronizationContext` so all WebSocket events fire on the game thread +automatically. + +```csharp +using System; +using System.Text; +using Microsoft.Xna.Framework; +using NativeWebSocket; +using NativeWebSocket.MonoGame; - websocket.OnMessage += (bytes) => +public class Game1 : Game +{ + private WebSocket _websocket; + + protected override void Initialize() { - Debug.Log("OnMessage!"); - Debug.Log(bytes); - - // getting the message as a string - // var message = System.Text.Encoding.UTF8.GetString(bytes); - // Debug.Log("OnMessage! " + message); - }; - - // Keep sending messages at every 0.3s - InvokeRepeating("SendWebSocketMessage", 0.0f, 0.3f); - - // waiting for messages - await websocket.Connect(); - } - - void Update() - { - #if !UNITY_WEBGL || UNITY_EDITOR - websocket.DispatchMessageQueue(); - #endif - } - - async void SendWebSocketMessage() - { - if (websocket.State == WebSocketState.Open) + Components.Add(new WebSocketGameComponent(this)); + base.Initialize(); + } + + protected override async void LoadContent() { - // Sending bytes - await websocket.Send(new byte[] { 10, 20, 30 }); + _websocket = new WebSocket("ws://localhost:3000"); + + _websocket.OnOpen += () => Console.WriteLine("Connected!"); + _websocket.OnError += (e) => Console.WriteLine("Error! " + e); + _websocket.OnClose += (code) => Console.WriteLine("Closed: " + code); - // Sending plain text - await websocket.SendText("plain text message"); + _websocket.OnMessage += (bytes) => + { + var message = Encoding.UTF8.GetString(bytes); + Console.WriteLine("Received: (" + bytes.Length + " bytes) " + message); + }; + + await _websocket.Connect(); } - } - private async void OnApplicationQuit() - { - await websocket.Close(); - } + protected override async void OnExiting(object sender, EventArgs args) + { + if (_websocket != null) + await _websocket.Close(); + base.OnExiting(sender, args); + } +} +``` + +## Godot + +Godot Mono has a built-in `GodotSynchronizationContext`, so no special +integration is needed. All WebSocket events fire on the main thread +automatically. + +```csharp +using System.Text; +using Godot; +using NativeWebSocket; + +public partial class WebSocketExample : Node +{ + private WebSocket _websocket; + + public override async void _Ready() + { + _websocket = new WebSocket("ws://localhost:3000"); + + _websocket.OnOpen += () => GD.Print("Connected!"); + _websocket.OnError += (e) => GD.Print("Error! " + e); + _websocket.OnClose += (code) => GD.Print("Closed: " + code); + + _websocket.OnMessage += (bytes) => + { + var message = Encoding.UTF8.GetString(bytes); + GD.Print("Received: (" + bytes.Length + " bytes) " + message); + }; + + await _websocket.Connect(); + } + public override void _ExitTree() + { + _websocket?.Close(); + } } ``` -# Demonstration +## Generic .NET (no SynchronizationContext) -**1.** Start the local WebSocket server: +If your environment doesn't have a `SynchronizationContext` (e.g. a console +app), call `DispatchMessageQueue()` from your main loop to process events: + +```csharp +var ws = new WebSocket("ws://localhost:3000"); +ws.OnMessage += (bytes) => Console.WriteLine("Received " + bytes.Length + " bytes"); +_ = ws.Connect(); +while (true) +{ + ws.DispatchMessageQueue(); + Thread.Sleep(16); +} ``` -cd Server + +# Examples + +Full runnable examples are in the [`examples/`](examples/) directory: + +| Engine | Path | How to run | +|--------|------|------------| +| **MonoGame** | [`examples/MonoGame/`](examples/MonoGame/) | `dotnet run --project examples/MonoGame/MonoGameExample.csproj` | +| **Godot** | [`examples/Godot/`](examples/Godot/) | Open in Godot Editor (4.x+ with C#), build, and press Play | +| **Unity** | [`examples/Unity/`](examples/Unity/) | Import NativeWebSocket via UPM, add `Connection.cs` to a GameObject | + +All examples connect to the included test server: + +```bash +cd node-websocket-server npm install npm start ``` -**2.** Open the `NativeWebSocket/Assets/WebSocketExample/WebSocketExampleScene.unity` on Unity and Run. +The server listens on `ws://localhost:3000`, sends periodic text and binary +messages, and logs anything received from the client. + +# API + +## Constructor + +```csharp +new WebSocket(string url) +new WebSocket(string url, Dictionary headers) +new WebSocket(string url, string subprotocol) +new WebSocket(string url, List subprotocols) +``` + +## Events + +| Event | Signature | Description | +|-------|-----------|-------------| +| `OnOpen` | `()` | Connection established | +| `OnMessage` | `(byte[] data)` | Message received (text or binary) | +| `OnError` | `(string errorMsg)` | Error occurred | +| `OnClose` | `(WebSocketCloseCode code)` | Connection closed | + +## Methods + +| Method | Description | +|--------|-------------| +| `Connect()` | Connect to the server (async) | +| `Close(code, reason)` | Gracefully close the connection (async) | +| `Send(byte[])` | Send binary data (async) | +| `SendText(string)` | Send text data (async) | +| `CancelConnection()` | Cancel a pending connection attempt | +| `DispatchMessageQueue()` | Manually dispatch queued events (only needed without a `SynchronizationContext`) | + +## Properties + +| Property | Type | Description | +|----------|------|-------------| +| `State` | `WebSocketState` | `Connecting`, `Open`, `Closing`, or `Closed` | +--- ## Acknowledgements diff --git a/examples/Godot/GodotExample.csproj.old b/examples/Godot/GodotExample.csproj.old new file mode 100644 index 0000000..f4c8121 --- /dev/null +++ b/examples/Godot/GodotExample.csproj.old @@ -0,0 +1,10 @@ + + + net6.0 + true + + + + + + diff --git a/examples/Godot/Main.tscn b/examples/Godot/Main.tscn new file mode 100644 index 0000000..2ab1e8b --- /dev/null +++ b/examples/Godot/Main.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://WebSocketExample.cs" id="1"] + +[node name="Main" type="Node"] +script = ExtResource("1") diff --git a/examples/Godot/WebSocketExample.cs b/examples/Godot/WebSocketExample.cs new file mode 100644 index 0000000..5e003bf --- /dev/null +++ b/examples/Godot/WebSocketExample.cs @@ -0,0 +1,65 @@ +using System.Text; +using Godot; +using NativeWebSocket; + +/// +/// Godot has a built-in GodotSynchronizationContext, so all WebSocket +/// events (OnOpen, OnMessage, OnError, OnClose) automatically fire on the main thread. +/// No special integration or DispatchMessageQueue() call is needed. +/// +public partial class WebSocketExample : Node +{ + private WebSocket _websocket; + private double _sendTimer; + + public override async void _Ready() + { + _websocket = new WebSocket("ws://localhost:3000"); + + _websocket.OnOpen += () => + { + GD.Print("Connection open!"); + }; + + _websocket.OnError += (e) => + { + GD.Print("Error! " + e); + }; + + _websocket.OnClose += (code) => + { + GD.Print("Connection closed! Code: " + code); + }; + + _websocket.OnMessage += (bytes) => + { + var message = Encoding.UTF8.GetString(bytes); + GD.Print("Received OnMessage! (" + bytes.Length + " bytes) " + message); + }; + + await _websocket.Connect(); + } + + public override async void _Process(double delta) + { + if (_websocket?.State == WebSocketState.Open) + { + _sendTimer += delta; + if (_sendTimer >= 0.3) + { + _sendTimer = 0; + + // Send binary data + await _websocket.Send(new byte[] { 10, 20, 30 }); + + // Send text data + await _websocket.SendText("hello from Godot!"); + } + } + } + + public override void _ExitTree() + { + _websocket?.Close(); + } +} diff --git a/examples/Godot/WebSocketExample.cs.uid b/examples/Godot/WebSocketExample.cs.uid new file mode 100644 index 0000000..bce9565 --- /dev/null +++ b/examples/Godot/WebSocketExample.cs.uid @@ -0,0 +1 @@ +uid://by13uhwlsa07w diff --git a/examples/Godot/project.godot b/examples/Godot/project.godot new file mode 100644 index 0000000..41c5f31 --- /dev/null +++ b/examples/Godot/project.godot @@ -0,0 +1,23 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[animation] + +compatibility/default_parent_skeleton_in_mesh_instance_3d=true + +[application] + +config/name="NativeWebSocket Example" +run/main_scene="res://Main.tscn" +config/features=PackedStringArray("4.6", "C#") + +[dotnet] + +project/assembly_name="GodotExample" diff --git a/examples/MonoGame/Game1.cs b/examples/MonoGame/Game1.cs new file mode 100644 index 0000000..e6f934d --- /dev/null +++ b/examples/MonoGame/Game1.cs @@ -0,0 +1,92 @@ +using System; +using System.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using NativeWebSocket; +using NativeWebSocket.MonoGame; + +namespace MonoGameExample +{ + public class Game1 : Game + { + private GraphicsDeviceManager _graphics; + private WebSocket _websocket; + private float _sendTimer; + + public Game1() + { + _graphics = new GraphicsDeviceManager(this); + IsMouseVisible = true; + } + + protected override void Initialize() + { + // Install the WebSocket game component. + // This sets up a SynchronizationContext so all WebSocket events + // fire on the game thread automatically. + Components.Add(new WebSocketGameComponent(this)); + base.Initialize(); + } + + protected override async void LoadContent() + { + _websocket = new WebSocket("ws://localhost:3000"); + + _websocket.OnOpen += () => + { + Console.WriteLine("Connection open!"); + }; + + _websocket.OnError += (e) => + { + Console.WriteLine("Error! " + e); + }; + + _websocket.OnClose += (code) => + { + Console.WriteLine("Connection closed! Code: " + code); + }; + + _websocket.OnMessage += (bytes) => + { + var message = Encoding.UTF8.GetString(bytes); + Console.WriteLine("Received OnMessage! (" + bytes.Length + " bytes) " + message); + }; + + await _websocket.Connect(); + } + + protected override async void Update(GameTime gameTime) + { + if (Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + // Send a message every 0.3 seconds + if (_websocket?.State == WebSocketState.Open) + { + _sendTimer += (float)gameTime.ElapsedGameTime.TotalSeconds; + if (_sendTimer >= 0.3f) + { + _sendTimer = 0; + + // Send binary data + await _websocket.Send(new byte[] { 10, 20, 30 }); + + // Send text data + await _websocket.SendText("hello from MonoGame!"); + } + } + + base.Update(gameTime); + } + + protected override async void OnExiting(object sender, EventArgs args) + { + if (_websocket != null) + { + await _websocket.Close(); + } + base.OnExiting(sender, args); + } + } +} diff --git a/examples/MonoGame/Program.cs b/examples/MonoGame/Program.cs new file mode 100644 index 0000000..877116b --- /dev/null +++ b/examples/MonoGame/Program.cs @@ -0,0 +1,2 @@ +using var game = new MonoGameExample.Game1(); +game.Run(); diff --git a/examples/Unity/Connection.cs b/examples/Unity/Connection.cs new file mode 100644 index 0000000..b0cd023 --- /dev/null +++ b/examples/Unity/Connection.cs @@ -0,0 +1,75 @@ +using System.Text; +using UnityEngine; +using NativeWebSocket; + +/// +/// Unity example — attach this script to a GameObject in your scene. +/// +/// Setup: +/// 1. Import NativeWebSocket via UPM: +/// Window > Package Manager > "+" > Add package from git URL: +/// https://github.com/endel/NativeWebSocket.git#upm +/// +/// 2. Create an empty GameObject and attach this script. +/// +/// 3. Start the test server: cd NodeServer && npm install && node index.js +/// +/// 4. Press Play. +/// +/// Unity's UnitySynchronizationContext handles event dispatching automatically, +/// so no DispatchMessageQueue() call is needed in Update(). +/// +public class Connection : MonoBehaviour +{ + WebSocket websocket; + + async void Start() + { + Application.runInBackground = true; + + websocket = new WebSocket("ws://localhost:3000"); + + websocket.OnOpen += () => + { + Debug.Log("Connection open!"); + }; + + websocket.OnError += (e) => + { + Debug.Log("Error! " + e); + }; + + websocket.OnClose += (code) => + { + Debug.Log("Connection closed! Code: " + code); + }; + + websocket.OnMessage += (bytes) => + { + var message = Encoding.UTF8.GetString(bytes); + Debug.Log("Received OnMessage! (" + bytes.Length + " bytes) " + message); + }; + + // Send messages every 0.3 seconds + InvokeRepeating("SendWebSocketMessage", 0.0f, 0.3f); + + await websocket.Connect(); + } + + async void SendWebSocketMessage() + { + if (websocket.State == WebSocketState.Open) + { + // Send binary data + await websocket.Send(new byte[] { 10, 20, 30 }); + + // Send text data + await websocket.SendText("hello from Unity!"); + } + } + + private async void OnApplicationQuit() + { + await websocket.Close(); + } +} diff --git a/integrations/Godot/Example.cs b/integrations/Godot/Example.cs new file mode 100644 index 0000000..605aa44 --- /dev/null +++ b/integrations/Godot/Example.cs @@ -0,0 +1,45 @@ +using Godot; +using NativeWebSocket; + +/// +/// Godot example - no special integration needed. +/// Godot Mono has a built-in GodotSynchronizationContext, so all WebSocket +/// events (OnOpen, OnMessage, OnError, OnClose) automatically fire on the main thread. +/// +public partial class WebSocketExample : Node +{ + private WebSocket _websocket; + + public override async void _Ready() + { + _websocket = new WebSocket("ws://localhost:3000"); + + _websocket.OnOpen += () => + { + GD.Print("Connection open!"); + }; + + _websocket.OnError += (e) => + { + GD.Print("Error! " + e); + }; + + _websocket.OnClose += (e) => + { + GD.Print("Connection closed!"); + }; + + _websocket.OnMessage += (bytes) => + { + var message = System.Text.Encoding.UTF8.GetString(bytes); + GD.Print("Received OnMessage! (" + bytes.Length + " bytes) " + message); + }; + + await _websocket.Connect(); + } + + public override void _ExitTree() + { + _websocket?.Close(); + } +} diff --git a/integrations/MonoGame/NativeWebSocket.MonoGame.csproj b/integrations/MonoGame/NativeWebSocket.MonoGame.csproj new file mode 100644 index 0000000..0be0584 --- /dev/null +++ b/integrations/MonoGame/NativeWebSocket.MonoGame.csproj @@ -0,0 +1,21 @@ + + + net6.0 + NativeWebSocket.MonoGame + NativeWebSocket.MonoGame + 2.0.0 + Endel Dreyer + MonoGame integration for NativeWebSocket - provides a GameComponent that enables automatic main-thread event dispatching. + Colyseus.NativeWebSocket.MonoGame + MIT + https://github.com/endel/NativeWebSocket + + + + + + + + + + diff --git a/integrations/MonoGame/WebSocketGameComponent.cs b/integrations/MonoGame/WebSocketGameComponent.cs new file mode 100644 index 0000000..6b84c19 --- /dev/null +++ b/integrations/MonoGame/WebSocketGameComponent.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using Microsoft.Xna.Framework; + +namespace NativeWebSocket.MonoGame +{ + /// + /// A SynchronizationContext that queues callbacks to be executed on the game thread. + /// + public class SingleThreadSynchronizationContext : SynchronizationContext + { + private readonly ConcurrentQueue _queue = new ConcurrentQueue(); + + public override void Post(SendOrPostCallback d, object state) + { + _queue.Enqueue(() => d(state)); + } + + public override void Send(SendOrPostCallback d, object state) + { + // Queue for execution on the game thread (same as Post for single-threaded pump) + _queue.Enqueue(() => d(state)); + } + + public void ProcessQueue() + { + while (_queue.TryDequeue(out var action)) + { + action(); + } + } + } + + /// + /// GameComponent that installs a SynchronizationContext on the game thread + /// and pumps queued callbacks each frame. + /// + /// Usage: + /// Components.Add(new WebSocketGameComponent(this)); + /// + /// After this, all NativeWebSocket events (OnOpen, OnMessage, OnError, OnClose) + /// will automatically fire on the game thread. + /// + public class WebSocketGameComponent : GameComponent + { + private readonly SingleThreadSynchronizationContext _syncContext; + + public WebSocketGameComponent(Game game) : base(game) + { + _syncContext = new SingleThreadSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(_syncContext); + } + + public override void Update(GameTime gameTime) + { + _syncContext.ProcessQueue(); + base.Update(gameTime); + } + } +} diff --git a/integrations/Unity/Samples~/WebSocketExample/Connection.cs b/integrations/Unity/Samples~/WebSocketExample/Connection.cs new file mode 100644 index 0000000..524feab --- /dev/null +++ b/integrations/Unity/Samples~/WebSocketExample/Connection.cs @@ -0,0 +1,61 @@ +using System; +using UnityEngine; + +using NativeWebSocket; + +public class Connection : MonoBehaviour +{ + WebSocket websocket; + + // Start is called before the first frame update + async void Start() + { + websocket = new WebSocket("ws://localhost:3000"); + + websocket.OnOpen += () => + { + Debug.Log("Connection open!"); + }; + + websocket.OnError += (e) => + { + Debug.Log("Error! " + e); + }; + + websocket.OnClose += (e) => + { + Debug.Log("Connection closed!"); + }; + + websocket.OnMessage += (bytes) => + { + // Reading a plain text message + var message = System.Text.Encoding.UTF8.GetString(bytes); + Debug.Log("Received OnMessage! (" + bytes.Length + " bytes) " + message); + }; + + // Keep sending messages at every 0.3s + InvokeRepeating("SendWebSocketMessage", 0.0f, 0.3f); + + // No DispatchMessageQueue() needed in Update()! + // Unity's SynchronizationContext handles event dispatching automatically. + await websocket.Connect(); + } + + async void SendWebSocketMessage() + { + if (websocket.State == WebSocketState.Open) + { + // Sending bytes + await websocket.Send(new byte[] { 10, 20, 30 }); + + // Sending plain text + await websocket.SendText("plain text message"); + } + } + + private async void OnApplicationQuit() + { + await websocket.Close(); + } +} diff --git a/NativeWebSocket/Assets/WebSocket/WebSocket.jslib b/integrations/Unity/WebSocket.jslib similarity index 100% rename from NativeWebSocket/Assets/WebSocket/WebSocket.jslib rename to integrations/Unity/WebSocket.jslib diff --git a/NativeWebSocket/Assets/WebSocket/WebSocket.jspre b/integrations/Unity/WebSocket.jspre similarity index 100% rename from NativeWebSocket/Assets/WebSocket/WebSocket.jspre rename to integrations/Unity/WebSocket.jspre diff --git a/integrations/Unity/WebSocketFactoryWebGL.cs b/integrations/Unity/WebSocketFactoryWebGL.cs new file mode 100644 index 0000000..c6ab811 --- /dev/null +++ b/integrations/Unity/WebSocketFactoryWebGL.cs @@ -0,0 +1,133 @@ +#if UNITY_WEBGL && !UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using AOT; + +namespace NativeWebSocket +{ + /// + /// Class providing static access methods to work with JSLIB WebSocket interface. + /// + public static class WebSocketFactory + { + /* Map of websocket instances */ + public static Dictionary instances = new Dictionary(); + + /* Delegates */ + public delegate void OnOpenCallback(int instanceId); + public delegate void OnMessageCallback(int instanceId, System.IntPtr msgPtr, int msgSize); + public delegate void OnErrorCallback(int instanceId, System.IntPtr errorPtr); + public delegate void OnCloseCallback(int instanceId, int closeCode); + + /* WebSocket JSLIB callback setters and other functions */ + [DllImport("__Internal")] + public static extern int WebSocketAllocate(string url); + + [DllImport("__Internal")] + public static extern int WebSocketAddSubProtocol(int instanceId, string subprotocol); + + [DllImport("__Internal")] + public static extern void WebSocketFree(int instanceId); + + [DllImport("__Internal")] + public static extern void WebSocketSetOnOpen(OnOpenCallback callback); + + [DllImport("__Internal")] + public static extern void WebSocketSetOnMessage(OnMessageCallback callback); + + [DllImport("__Internal")] + public static extern void WebSocketSetOnError(OnErrorCallback callback); + + [DllImport("__Internal")] + public static extern void WebSocketSetOnClose(OnCloseCallback callback); + + /* If callbacks was initialized and set */ + public static bool isInitialized = false; + + /* + * Initialize WebSocket callbacks to JSLIB + */ + public static void Initialize() + { + WebSocketSetOnOpen(DelegateOnOpenEvent); + WebSocketSetOnMessage(DelegateOnMessageEvent); + WebSocketSetOnError(DelegateOnErrorEvent); + WebSocketSetOnClose(DelegateOnCloseEvent); + + isInitialized = true; + } + + /// + /// Called when instance is destroyed (by destructor) + /// Method removes instance from map and free it in JSLIB implementation + /// + /// Instance identifier. + public static void HandleInstanceDestroy(int instanceId) + { + instances.Remove(instanceId); + WebSocketFree(instanceId); + } + + [MonoPInvokeCallback(typeof(OnOpenCallback))] + public static void DelegateOnOpenEvent(int instanceId) + { + WebSocket instanceRef; + + if (instances.TryGetValue(instanceId, out instanceRef)) + { + instanceRef.DelegateOnOpenEvent(); + } + } + + [MonoPInvokeCallback(typeof(OnMessageCallback))] + public static void DelegateOnMessageEvent(int instanceId, System.IntPtr msgPtr, int msgSize) + { + WebSocket instanceRef; + + if (instances.TryGetValue(instanceId, out instanceRef)) + { + byte[] msg = new byte[msgSize]; + Marshal.Copy(msgPtr, msg, 0, msgSize); + + instanceRef.DelegateOnMessageEvent(msg); + } + } + + [MonoPInvokeCallback(typeof(OnErrorCallback))] + public static void DelegateOnErrorEvent(int instanceId, System.IntPtr errorPtr) + { + WebSocket instanceRef; + + if (instances.TryGetValue(instanceId, out instanceRef)) + { + string errorMsg = Marshal.PtrToStringAuto(errorPtr); + instanceRef.DelegateOnErrorEvent(errorMsg); + } + } + + [MonoPInvokeCallback(typeof(OnCloseCallback))] + public static void DelegateOnCloseEvent(int instanceId, int closeCode) + { + WebSocket instanceRef; + + if (instances.TryGetValue(instanceId, out instanceRef)) + { + instanceRef.DelegateOnCloseEvent(closeCode); + } + } + + /// + /// Create WebSocket client instance + /// + /// The WebSocket instance. + /// WebSocket valid URL. + public static WebSocket CreateInstance(string url) + { + return new WebSocket(url); + } + } +} + +#endif diff --git a/integrations/Unity/WebSocketWebGL.cs b/integrations/Unity/WebSocketWebGL.cs new file mode 100644 index 0000000..4033501 --- /dev/null +++ b/integrations/Unity/WebSocketWebGL.cs @@ -0,0 +1,191 @@ +#if UNITY_WEBGL && !UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace NativeWebSocket +{ + /// + /// WebSocket class bound to JSLIB. + /// + public class WebSocket : IWebSocket + { + /* WebSocket JSLIB functions */ + [DllImport("__Internal")] + public static extern int WebSocketConnect(int instanceId); + + [DllImport("__Internal")] + public static extern int WebSocketClose(int instanceId, int code, string reason); + + [DllImport("__Internal")] + public static extern int WebSocketSend(int instanceId, byte[] dataPtr, int dataLength); + + [DllImport("__Internal")] + public static extern int WebSocketSendText(int instanceId, string message); + + [DllImport("__Internal")] + public static extern int WebSocketGetState(int instanceId); + + protected int instanceId; + + public event WebSocketOpenEventHandler OnOpen; + public event WebSocketMessageEventHandler OnMessage; + public event WebSocketErrorEventHandler OnError; + public event WebSocketCloseEventHandler OnClose; + + public WebSocket(string url, Dictionary headers = null) + { + if (!WebSocketFactory.isInitialized) + { + WebSocketFactory.Initialize(); + } + + int instanceId = WebSocketFactory.WebSocketAllocate(url); + WebSocketFactory.instances.Add(instanceId, this); + + this.instanceId = instanceId; + } + + public WebSocket(string url, string subprotocol, Dictionary headers = null) + { + if (!WebSocketFactory.isInitialized) + { + WebSocketFactory.Initialize(); + } + + int instanceId = WebSocketFactory.WebSocketAllocate(url); + WebSocketFactory.instances.Add(instanceId, this); + + WebSocketFactory.WebSocketAddSubProtocol(instanceId, subprotocol); + + this.instanceId = instanceId; + } + + public WebSocket(string url, List subprotocols, Dictionary headers = null) + { + if (!WebSocketFactory.isInitialized) + { + WebSocketFactory.Initialize(); + } + + int instanceId = WebSocketFactory.WebSocketAllocate(url); + WebSocketFactory.instances.Add(instanceId, this); + + foreach (string subprotocol in subprotocols) + { + WebSocketFactory.WebSocketAddSubProtocol(instanceId, subprotocol); + } + + this.instanceId = instanceId; + } + + ~WebSocket() + { + WebSocketFactory.HandleInstanceDestroy(this.instanceId); + } + + public int GetInstanceId() + { + return this.instanceId; + } + + public Task Connect() + { + int ret = WebSocketConnect(this.instanceId); + + if (ret < 0) + throw WebSocketHelpers.GetErrorMessageFromCode(ret, null); + + return Task.CompletedTask; + } + + public void CancelConnection() + { + if (State == WebSocketState.Open) + Close(WebSocketCloseCode.Abnormal); + } + + public Task Close(WebSocketCloseCode code = WebSocketCloseCode.Normal, string reason = null) + { + int ret = WebSocketClose(this.instanceId, (int)code, reason); + + if (ret < 0) + throw WebSocketHelpers.GetErrorMessageFromCode(ret, null); + + return Task.CompletedTask; + } + + public Task Send(byte[] data) + { + int ret = WebSocketSend(this.instanceId, data, data.Length); + + if (ret < 0) + throw WebSocketHelpers.GetErrorMessageFromCode(ret, null); + + return Task.CompletedTask; + } + + public Task SendText(string message) + { + int ret = WebSocketSendText(this.instanceId, message); + + if (ret < 0) + throw WebSocketHelpers.GetErrorMessageFromCode(ret, null); + + return Task.CompletedTask; + } + + public WebSocketState State + { + get + { + int state = WebSocketGetState(this.instanceId); + + if (state < 0) + throw WebSocketHelpers.GetErrorMessageFromCode(state, null); + + switch (state) + { + case 0: + return WebSocketState.Connecting; + + case 1: + return WebSocketState.Open; + + case 2: + return WebSocketState.Closing; + + case 3: + return WebSocketState.Closed; + + default: + return WebSocketState.Closed; + } + } + } + + public void DelegateOnOpenEvent() + { + this.OnOpen?.Invoke(); + } + + public void DelegateOnMessageEvent(byte[] data) + { + this.OnMessage?.Invoke(data); + } + + public void DelegateOnErrorEvent(string errorMsg) + { + this.OnError?.Invoke(errorMsg); + } + + public void DelegateOnCloseEvent(int closeCode) + { + this.OnClose?.Invoke(WebSocketHelpers.ParseCloseCodeEnum(closeCode)); + } + } +} + +#endif diff --git a/NativeWebSocket/Assets/WebSocket/endel.nativewebsocket.asmdef b/integrations/Unity/endel.nativewebsocket.asmdef similarity index 81% rename from NativeWebSocket/Assets/WebSocket/endel.nativewebsocket.asmdef rename to integrations/Unity/endel.nativewebsocket.asmdef index 9d477f8..b8b3f57 100644 --- a/NativeWebSocket/Assets/WebSocket/endel.nativewebsocket.asmdef +++ b/integrations/Unity/endel.nativewebsocket.asmdef @@ -1,3 +1,3 @@ -{ - "name": "endel.nativewebsocket" -} +{ + "name": "endel.nativewebsocket" +} diff --git a/NativeWebSocket/Assets/package.json b/integrations/Unity/package.json similarity index 78% rename from NativeWebSocket/Assets/package.json rename to integrations/Unity/package.json index ca878de..b23f0bd 100644 --- a/NativeWebSocket/Assets/package.json +++ b/integrations/Unity/package.json @@ -1,8 +1,8 @@ { "name": "com.endel.nativewebsocket", - "version": "1.1.6", - "description": "WebSocket client for Unity - with no external dependencies (WebGL, Native, Android, iOS, UWP).", - "license": "Apache 2.0", + "version": "2.0.0", + "description": "WebSocket client for Unity - with no external dependencies (WebGL, Native, Android, iOS, UWP). Events auto-dispatch to the main thread via SynchronizationContext.", + "license": "MIT", "repository": { "type": "git", "url": "https://github.com/endel/NativeWebSocket.git" @@ -22,9 +22,10 @@ "unity": "2019.1", "dependencies": {}, "samples": [ - { + { "displayName": "Example", "description": "WebSocket Example", "path": "Samples~/WebSocketExample" - } ] + } + ] } diff --git a/NodeServer/index.js b/node-websocket-server/index.js similarity index 82% rename from NodeServer/index.js rename to node-websocket-server/index.js index 0428917..50533f1 100644 --- a/NodeServer/index.js +++ b/node-websocket-server/index.js @@ -1,13 +1,13 @@ -const crypto = require('crypto'); -const express = require('express'); -const { createServer } = require('http'); -const WebSocket = require('ws'); +import crypto from 'crypto'; +import express from 'express'; +import { createServer } from 'http'; +import { WebSocketServer } from 'ws'; const app = express(); const port = 3000; const server = createServer(app); -const wss = new WebSocket.Server({ server }); +const wss = new WebSocketServer({ server }); wss.on('connection', function(ws) { console.log("client joined."); diff --git a/node-websocket-server/package-lock.json b/node-websocket-server/package-lock.json new file mode 100644 index 0000000..24f4779 --- /dev/null +++ b/node-websocket-server/package-lock.json @@ -0,0 +1,1359 @@ +{ + "name": "nodeserver", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "nodeserver", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^5.1.0", + "ws": "^8.18.1" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "requires": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + } + }, + "body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "requires": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==" + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + }, + "cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==" + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "requires": { + "ms": "^2.1.3" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "requires": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + } + }, + "finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "requires": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==" + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "requires": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + } + }, + "iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, + "media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==" + }, + "merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==" + }, + "mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==" + }, + "mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "requires": { + "mime-db": "^1.54.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==" + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "requires": { + "side-channel": "^1.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "requires": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + } + }, + "router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "requires": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "requires": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + } + }, + "serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "requires": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "requires": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "requires": {} + } + } +} diff --git a/NodeServer/package.json b/node-websocket-server/package.json similarity index 75% rename from NodeServer/package.json rename to node-websocket-server/package.json index 16102b1..503eb4c 100644 --- a/NodeServer/package.json +++ b/node-websocket-server/package.json @@ -2,18 +2,16 @@ "name": "nodeserver", "version": "1.0.0", "description": "", + "type": "module", "main": "index.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, - "engines": { - "node": "12.x" - }, "author": "", "license": "ISC", "dependencies": { - "express": "^4.17.1", - "ws": "^7.1.2" + "express": "^5.1.0", + "ws": "^8.18.1" } } diff --git a/src/NativeWebSocket/IWebSocket.cs b/src/NativeWebSocket/IWebSocket.cs new file mode 100644 index 0000000..30f587e --- /dev/null +++ b/src/NativeWebSocket/IWebSocket.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; + +namespace NativeWebSocket +{ + public delegate void WebSocketOpenEventHandler(); + public delegate void WebSocketMessageEventHandler(byte[] data); + public delegate void WebSocketErrorEventHandler(string errorMsg); + public delegate void WebSocketCloseEventHandler(WebSocketCloseCode closeCode); + + public enum WebSocketCloseCode + { + /* Do NOT use NotSet - it's only purpose is to indicate that the close code cannot be parsed. */ + NotSet = 0, + Normal = 1000, + Away = 1001, + ProtocolError = 1002, + UnsupportedData = 1003, + Undefined = 1004, + NoStatus = 1005, + Abnormal = 1006, + InvalidData = 1007, + PolicyViolation = 1008, + TooBig = 1009, + MandatoryExtension = 1010, + ServerError = 1011, + TlsHandshakeFailure = 1015 + } + + public enum WebSocketState + { + Connecting, + Open, + Closing, + Closed + } + + public interface IWebSocket + { + event WebSocketOpenEventHandler OnOpen; + event WebSocketMessageEventHandler OnMessage; + event WebSocketErrorEventHandler OnError; + event WebSocketCloseEventHandler OnClose; + + WebSocketState State { get; } + + Task Connect(); + Task Close(WebSocketCloseCode code = WebSocketCloseCode.Normal, string reason = null); + Task Send(byte[] data); + Task SendText(string message); + } +} diff --git a/src/NativeWebSocket/NativeWebSocket.csproj b/src/NativeWebSocket/NativeWebSocket.csproj new file mode 100644 index 0000000..f0106db --- /dev/null +++ b/src/NativeWebSocket/NativeWebSocket.csproj @@ -0,0 +1,14 @@ + + + netstandard2.0;net6.0 + NativeWebSocket + NativeWebSocket + 2.0.0 + Endel Dreyer + WebSocket client for .NET - works with Unity, MonoGame, Godot, and any .NET application. + Colyseus.NativeWebSocket + MIT + https://github.com/endel/NativeWebSocket + 7.3 + + diff --git a/src/NativeWebSocket/WebSocket.cs b/src/NativeWebSocket/WebSocket.cs new file mode 100644 index 0000000..2946d88 --- /dev/null +++ b/src/NativeWebSocket/WebSocket.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NativeWebSocket +{ + public class WebSocket : IWebSocket + { + public event WebSocketOpenEventHandler OnOpen; + public event WebSocketMessageEventHandler OnMessage; + public event WebSocketErrorEventHandler OnError; + public event WebSocketCloseEventHandler OnClose; + + private Uri uri; + private Dictionary headers; + private List subprotocols; + private ClientWebSocket m_Socket = new ClientWebSocket(); + + private CancellationTokenSource m_TokenSource; + private CancellationToken m_CancellationToken; + + private readonly SynchronizationContext _syncContext; + private List m_EventQueue = new List(); + private List m_DispatchQueue = new List(); + private readonly object EventQueueLock = new object(); + + private readonly object OutgoingMessageLock = new object(); + private bool isSending = false; + private Queue> sendBytesQueue = new Queue>(); + private Queue> sendTextQueue = new Queue>(); + + public WebSocket(string url, Dictionary headers = null) + { + uri = new Uri(url); + _syncContext = SynchronizationContext.Current; + this.headers = headers ?? new Dictionary(); + subprotocols = new List(); + + string protocol = uri.Scheme; + if (!protocol.Equals("ws") && !protocol.Equals("wss")) + throw new ArgumentException("Unsupported protocol: " + protocol); + } + + public WebSocket(string url, string subprotocol, Dictionary headers = null) + { + uri = new Uri(url); + _syncContext = SynchronizationContext.Current; + this.headers = headers ?? new Dictionary(); + subprotocols = new List { subprotocol }; + + string protocol = uri.Scheme; + if (!protocol.Equals("ws") && !protocol.Equals("wss")) + throw new ArgumentException("Unsupported protocol: " + protocol); + } + + public WebSocket(string url, List subprotocols, Dictionary headers = null) + { + uri = new Uri(url); + _syncContext = SynchronizationContext.Current; + this.headers = headers ?? new Dictionary(); + this.subprotocols = subprotocols; + + string protocol = uri.Scheme; + if (!protocol.Equals("ws") && !protocol.Equals("wss")) + throw new ArgumentException("Unsupported protocol: " + protocol); + } + + private void EnqueueEvent(Action action) + { + if (_syncContext != null) + { + _syncContext.Post(_ => action(), null); + } + else + { + lock (EventQueueLock) + { + m_EventQueue.Add(action); + } + } + } + + /// + /// Dispatches queued events when no SynchronizationContext is available. + /// Not needed when a SynchronizationContext is present (Unity, Godot, MonoGame with WebSocketGameComponent). + /// + public void DispatchMessageQueue() + { + if (m_EventQueue.Count == 0) return; + + lock (EventQueueLock) + { + var tmp = m_DispatchQueue; + m_DispatchQueue = m_EventQueue; + m_EventQueue = tmp; + } + + foreach (var action in m_DispatchQueue) + { + action(); + } + + m_DispatchQueue.Clear(); + } + + public void CancelConnection() + { + m_TokenSource?.Cancel(); + } + + public async Task Connect() + { + try + { + m_TokenSource = new CancellationTokenSource(); + m_CancellationToken = m_TokenSource.Token; + + m_Socket = new ClientWebSocket(); + + foreach (var header in headers) + { + m_Socket.Options.SetRequestHeader(header.Key, header.Value); + } + + foreach (string subprotocol in subprotocols) + { + m_Socket.Options.AddSubProtocol(subprotocol); + } + + await m_Socket.ConnectAsync(uri, m_CancellationToken).ConfigureAwait(false); + + EnqueueEvent(() => OnOpen?.Invoke()); + + await Receive().ConfigureAwait(false); + } + catch (Exception ex) + { + EnqueueEvent(() => OnError?.Invoke(ex.Message)); + EnqueueEvent(() => OnClose?.Invoke(WebSocketCloseCode.Abnormal)); + } + finally + { + if (m_Socket != null) + { + m_TokenSource.Cancel(); + m_Socket.Dispose(); + } + } + } + + public WebSocketState State + { + get + { + switch (m_Socket.State) + { + case System.Net.WebSockets.WebSocketState.Connecting: + return WebSocketState.Connecting; + + case System.Net.WebSockets.WebSocketState.Open: + return WebSocketState.Open; + + case System.Net.WebSockets.WebSocketState.CloseSent: + case System.Net.WebSockets.WebSocketState.CloseReceived: + return WebSocketState.Closing; + + case System.Net.WebSockets.WebSocketState.Closed: + return WebSocketState.Closed; + + default: + return WebSocketState.Closed; + } + } + } + + public Task Send(byte[] bytes) + { + return SendMessage(sendBytesQueue, WebSocketMessageType.Binary, new ArraySegment(bytes)); + } + + public Task SendText(string message) + { + var encoded = Encoding.UTF8.GetBytes(message); + return SendMessage(sendTextQueue, WebSocketMessageType.Text, new ArraySegment(encoded, 0, encoded.Length)); + } + + private async Task SendMessage(Queue> queue, WebSocketMessageType messageType, ArraySegment buffer) + { + if (buffer.Count == 0 || State != WebSocketState.Open) + { + return; + } + + bool sending; + + lock (OutgoingMessageLock) + { + sending = isSending; + + if (!isSending) + { + isSending = true; + } + } + + if (!sending) + { + try + { + await m_Socket.SendAsync(buffer, messageType, true, m_CancellationToken).ConfigureAwait(false); + + // Drain the queue iteratively instead of recursively + while (true) + { + ArraySegment next; + lock (OutgoingMessageLock) + { + if (queue.Count == 0) + break; + next = queue.Dequeue(); + } + + await m_Socket.SendAsync(next, messageType, true, m_CancellationToken).ConfigureAwait(false); + } + } + finally + { + lock (OutgoingMessageLock) + { + isSending = false; + } + } + } + else + { + lock (OutgoingMessageLock) + { + queue.Enqueue(buffer); + } + } + } + + public async Task Receive() + { + WebSocketCloseCode closeCode = WebSocketCloseCode.Abnormal; + ArraySegment buffer = new ArraySegment(new byte[8192]); + try + { + while (m_Socket.State == System.Net.WebSockets.WebSocketState.Open) + { + var result = await m_Socket.ReceiveAsync(buffer, m_CancellationToken).ConfigureAwait(false); + + if (result.MessageType == WebSocketMessageType.Close) + { + await Close().ConfigureAwait(false); + closeCode = WebSocketHelpers.ParseCloseCodeEnum((int)result.CloseStatus); + break; + } + + if (result.EndOfMessage) + { + // Fast path: single-frame message, avoid MemoryStream + var data = new byte[result.Count]; + Buffer.BlockCopy(buffer.Array, buffer.Offset, data, 0, result.Count); + EnqueueEvent(() => OnMessage?.Invoke(data)); + } + else + { + // Multi-frame message: accumulate in MemoryStream + using (var ms = new MemoryStream()) + { + ms.Write(buffer.Array, buffer.Offset, result.Count); + + do + { + result = await m_Socket.ReceiveAsync(buffer, m_CancellationToken).ConfigureAwait(false); + ms.Write(buffer.Array, buffer.Offset, result.Count); + } + while (!result.EndOfMessage); + + var data = ms.ToArray(); + EnqueueEvent(() => OnMessage?.Invoke(data)); + } + } + } + } + catch (Exception) + { + m_TokenSource.Cancel(); + } + finally + { + EnqueueEvent(() => OnClose?.Invoke(closeCode)); + } + } + + public async Task Close(WebSocketCloseCode code = WebSocketCloseCode.Normal, string reason = null) + { + if (State != WebSocketState.Open) + return; + + await m_Socket.CloseAsync((WebSocketCloseStatus)(int)code, reason ?? string.Empty, CancellationToken.None).ConfigureAwait(false); + } + } + + /// + /// Factory for creating WebSocket instances. + /// + public static class WebSocketFactory + { + /// + /// Create WebSocket client instance + /// + /// The WebSocket instance. + /// WebSocket valid URL. + public static WebSocket CreateInstance(string url) + { + return new WebSocket(url); + } + } +} diff --git a/src/NativeWebSocket/WebSocketTypes.cs b/src/NativeWebSocket/WebSocketTypes.cs new file mode 100644 index 0000000..8055b3b --- /dev/null +++ b/src/NativeWebSocket/WebSocketTypes.cs @@ -0,0 +1,70 @@ +using System; + +namespace NativeWebSocket +{ + public static class WebSocketHelpers + { + public static WebSocketCloseCode ParseCloseCodeEnum(int closeCode) + { + if (WebSocketCloseCode.IsDefined(typeof(WebSocketCloseCode), closeCode)) + { + return (WebSocketCloseCode)closeCode; + } + else + { + return WebSocketCloseCode.Undefined; + } + } + + public static WebSocketException GetErrorMessageFromCode(int errorCode, Exception inner) + { + switch (errorCode) + { + case -1: + return new WebSocketUnexpectedException("WebSocket instance not found.", inner); + case -2: + return new WebSocketInvalidStateException("WebSocket is already connected or in connecting state.", inner); + case -3: + return new WebSocketInvalidStateException("WebSocket is not connected.", inner); + case -4: + return new WebSocketInvalidStateException("WebSocket is already closing.", inner); + case -5: + return new WebSocketInvalidStateException("WebSocket is already closed.", inner); + case -6: + return new WebSocketInvalidStateException("WebSocket is not in open state.", inner); + case -7: + return new WebSocketInvalidArgumentException("Cannot close WebSocket. An invalid code was specified or reason is too long.", inner); + default: + return new WebSocketUnexpectedException("Unknown error.", inner); + } + } + } + + public class WebSocketException : Exception + { + public WebSocketException() { } + public WebSocketException(string message) : base(message) { } + public WebSocketException(string message, Exception inner) : base(message, inner) { } + } + + public class WebSocketUnexpectedException : WebSocketException + { + public WebSocketUnexpectedException() { } + public WebSocketUnexpectedException(string message) : base(message) { } + public WebSocketUnexpectedException(string message, Exception inner) : base(message, inner) { } + } + + public class WebSocketInvalidArgumentException : WebSocketException + { + public WebSocketInvalidArgumentException() { } + public WebSocketInvalidArgumentException(string message) : base(message) { } + public WebSocketInvalidArgumentException(string message, Exception inner) : base(message, inner) { } + } + + public class WebSocketInvalidStateException : WebSocketException + { + public WebSocketInvalidStateException() { } + public WebSocketInvalidStateException(string message) : base(message) { } + public WebSocketInvalidStateException(string message, Exception inner) : base(message, inner) { } + } +}