├── EdgeMultiplay.unitypackage
├── EdgeMultiplay
├── Examples
│ ├── ChatRooms.unitypackage
│ ├── CreateSyncedObj.unitypackage
│ ├── PingPongExample.unitypackage
│ ├── OwnershipExamples.unitypackage
│ ├── PlayerTagsExample.unitypackage
│ ├── ChatRooms.unitypackage.meta
│ ├── CreateSyncedObj.unitypackage.meta
│ ├── OwnershipExamples.unitypackage.meta
│ ├── PingPongExample.unitypackage.meta
│ └── PlayerTagsExample.unitypackage.meta
├── Logo
│ ├── MobiledgeX_Icon_Green_Black.png
│ └── MobiledgeX_Icon_Green_Black.png.meta
├── CHANGELOG.md.meta
├── README.md.meta
├── Logo.meta
├── Editor.meta
├── Examples.meta
├── Scripts.meta
├── Resources
│ ├── ClientSettings.asset.meta
│ └── ClientSettings.asset
├── Scripts
│ ├── Util.cs.meta
│ ├── EdgeMultiplayCallbacks.cs.meta
│ ├── ClientSettings.cs.meta
│ ├── DataContracts.cs.meta
│ ├── EdgeManager.cs.meta
│ ├── NetworkedPlayer.cs.meta
│ ├── ObservableView.cs.meta
│ ├── OrphanObservable.cs.meta
│ ├── EdgeMultiplayObserver.cs.meta
│ ├── OrphanObservable.cs
│ ├── ClientSettings.cs
│ ├── ObservableView.cs
│ ├── Util.cs
│ ├── EdgeMultiplayCallbacks.cs
│ ├── EdgeMultiplayObserver.cs
│ ├── NetworkedPlayer.cs
│ ├── DataContracts.cs
│ └── EdgeManager.cs
├── Editor
│ ├── TemplateGenerator.cs.meta
│ ├── EdgeMultiplayEditorWindow.cs.meta
│ ├── EdgeMultiplayEditorWindow.cs
│ └── TemplateGenerator.cs
├── README.md
└── CHANGELOG.md
├── License.md
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── .gitignore
└── README.md
/EdgeMultiplay.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay.unitypackage
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/ChatRooms.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay/Examples/ChatRooms.unitypackage
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/CreateSyncedObj.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay/Examples/CreateSyncedObj.unitypackage
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/PingPongExample.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay/Examples/PingPongExample.unitypackage
--------------------------------------------------------------------------------
/EdgeMultiplay/Logo/MobiledgeX_Icon_Green_Black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay/Logo/MobiledgeX_Icon_Green_Black.png
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/OwnershipExamples.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay/Examples/OwnershipExamples.unitypackage
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/PlayerTagsExample.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobiledgex/edge-multiplay-unity-client/HEAD/EdgeMultiplay/Examples/PlayerTagsExample.unitypackage
--------------------------------------------------------------------------------
/EdgeMultiplay/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c2fe01ccd3c4f430b9b01e0ecb5db2ed
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1d0964b4287884a838d149aaf6e47f9b
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Logo.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 21f65ab52044b44f1bc12310e54ba08f
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5ba8eb8cb4a65416a9b43c09175777b3
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1136a6a873ebb4f8abbb390cbd1e153c
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/ChatRooms.unitypackage.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9d4b78dab91914f2bae094a7fb14c5e1
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2213faac1190a4aceb8300a7636b1949
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/CreateSyncedObj.unitypackage.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bdf2923fe4a424321b6b28536875606f
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/OwnershipExamples.unitypackage.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2eb647807aa4f42b6a85368b54b39be3
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/PingPongExample.unitypackage.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 33837d54e339943289dc3161129fadcf
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Examples/PlayerTagsExample.unitypackage.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5a35455f76929429a9990a725f5505a8
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Resources/ClientSettings.asset.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f03ae220e86bb4bf085351cad47307aa
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 11400000
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/Util.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: dda4aa854f0f348deba999f8a1c17898
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/EdgeMultiplayCallbacks.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f7437c7a6c65c4bcb9f1d895da82b2ed
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon:
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/ClientSettings.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7def378b872384b5b9526675226ca841
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/DataContracts.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 037d2555e62ff4453a856a154f442338
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Editor/TemplateGenerator.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 48297db3f38cf4f9e968c7c128b4d44f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Editor/EdgeMultiplayEditorWindow.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 467884a11b4fe4afc80c265d46d20b40
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/EdgeManager.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f6647f8771f53471d9d27799b03112a0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: f6328cde825c8425190e0ad08c7b3602, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/NetworkedPlayer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 30ed73c13f422450ab5056537c9ac3df
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: f6328cde825c8425190e0ad08c7b3602, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/ObservableView.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 73bb1828891e84c5c8a99ab171b54a09
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: f6328cde825c8425190e0ad08c7b3602, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/OrphanObservable.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e71c409e7249c44a98033e8881038970
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: f6328cde825c8425190e0ad08c7b3602, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/EdgeMultiplayObserver.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a804ed59794df49859448f348fea5a6c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: f6328cde825c8425190e0ad08c7b3602, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Resources/ClientSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &11400000
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 11500000, guid: ef8ab0f75f1374aeb8bd6b39cb726256, type: 3}
13 | m_Name: ClientSettings
14 | m_EditorClassIdentifier:
15 | useLocalHostServer: 0
16 | hostIPAddress: 127.0.0.1
17 | WebSocketPort: 3000
18 | StatisticsPort: 7776
19 | UDPPort: 5000
20 |
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
2 | MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: Feature Request
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/EdgeMultiplay/README.md:
--------------------------------------------------------------------------------
1 | To Connect your Unity Project to EdgeMultiplay Server, You have 3 options:
2 |
3 | 1. Using our sample EdgeMultiplay server:
4 | From Unity Toolbar Select MobiledgeX/Setup:
5 | OrgName: MobiledgeX-Samples
6 | AppName: EdgeMultiplay
7 | AppVers: (Same as EdgeMultiplay/Version in Unity Toolbar)
8 |
9 | 2. Run the server locally on your machine:
10 | for more instructions, please refer to https://github.com/mobiledgex/edge-multiplay-node-server.
11 |
12 | 3. You can deploy your server to MobiledgeX and use your own server defintions.
13 |
14 | *Note:
15 | For option 1 you will be using a shared server between other developers.
16 |
17 | If you encountered problems, please reach out to us on discord
18 | https://discord.com/invite/CHCWfgrxh6
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Ll]ibrary/
2 | [Tt]emp/
3 | [Oo]bj/
4 | [Bb]uild/
5 | [Bb]uilds/
6 | [Ll]ogs/
7 |
8 | # Uncomment this line if you wish to ignore the asset store tools plugin
9 | # [Aa]ssets/AssetStoreTools*
10 |
11 | # Visual Studio cache directory
12 | .vs/
13 |
14 | # Visual Studio Code
15 | .vscode/
16 |
17 | # Gradle cache directory
18 | .gradle/
19 |
20 | # Autogenerated VS/MD/Consulo solution and project files
21 | ExportedObj/
22 | .consulo/
23 | *.csproj
24 | *.unityproj
25 | *.sln
26 | *.suo
27 | *.tmp
28 | *.user
29 | *.userprefs
30 | *.pidb
31 | *.booproj
32 | *.svd
33 | *.pdb
34 | *.mdb
35 | *.opendb
36 | *.VC.db
37 | *.DS_Store
38 | # Unity3D generated meta files
39 | *.pidb.meta
40 | *.pdb.meta
41 | *.mdb.meta
42 |
43 | # Unity3D generated file on crash reports
44 | sysinfo.txt
45 |
46 | # Builds
47 | *.apk
48 |
49 | # Crashlytics generated file
50 | crashlytics-build.properties
51 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Version [e.g. 22]
29 |
30 | **Smartphone (please complete the following information):**
31 | - Device: [e.g. iPhone6]
32 | - OS: [e.g. iOS8.1]
33 | - Version [e.g. 22]
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | # EdgeMultiplay-UnityClient
12 |
13 | EdgeMultiplay is a simple solution for creating multiplayer experiences on Edge.
14 | This repository contains the unity client of the EdgeMultiplay Solution.
15 |
16 | ## Prerequisites
17 |
18 | * Unity 2019.2 or newer, along with selected platforms (iOS, Android) for your project
19 | * The Solution is compatible with (IL2CPP & .NET 2.0) , (IL2CPP & .NET 4.x) , (Mono & .NET 2.0) **but not compatible with (Mono & .NET 4.x)**
20 | * [MobiledgeX Unity SDK](https://github.com/mobiledgex/edge-cloud-sdk-unity).
21 |
22 | ## Documentation
23 | To get started, Check [EdgeMultiplay Github Page ](https://mobiledgex.github.io/edge-multiplay-unity-client/)
24 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/OrphanObservable.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using UnityEngine;
19 |
20 | namespace EdgeMultiplay
21 | {
22 | ///
23 | /// Add OrphanObservable to GameObjects that don't have an owner at the time of creation (Ex. created in Unity Editor)
24 | /// the default owner of this observable is the first player in the room, You can change this in SetOwnerShip() below
25 | ///
26 | [AddComponentMenu("EdgeMultiplay/OrphanObservable")]
27 | public class OrphanObservable : MonoBehaviour
28 | {
29 | public Observable observable;
30 |
31 | void Update()
32 | {
33 | if (observable != null && observable.owner == null)
34 | {
35 | if (EdgeManager.gameStarted)
36 | {
37 | observable.SetOwnership(EdgeManager.GetPlayer(0).playerId);//owner is first player in the room
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/ClientSettings.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System;
3 | namespace EdgeMultiplay
4 | {
5 | [Serializable]
6 | public class ClientSettings : ScriptableObject
7 | {
8 | [Header("Host Configuration", order = 10)]
9 | ///
10 | /// Set to true if you have EdgeMultiplay Server running on your machine
11 | ///
12 | [Tooltip("Set to true if you have EdgeMultiplay Server running on your machine")]
13 | public bool useLocalHostServer;
14 |
15 | ///
16 | /// Use 127.0.0.1 as your IP Address for testing on your computer only
17 | /// Or use the Host IP Address for testing between your Computer and devices connected to the same WifiNetwork
18 | /// For Mac : Open Terminal and type "ifconfig" and copy the "en0" address
19 | /// For Windows : Open CMD and type "Ipconfig /all" and copy the "IPV4" address
20 | ///
21 | [Tooltip("Use 127.0.0.1 as your IP Address for testing on you computer only,\n" +
22 | "Or use the Host IP Address for testing between your Computer and devices connected to the same WifiNetwork\n" +
23 | "For Mac : Open Terminal and type \"ifconfig\" and copy the \"en0\" address \n" +
24 | "For Windows : Open CMD and type \"Ipconfig /all \" and copy the \"IPV4\" address ")]
25 | public string hostIPAddress;
26 |
27 | [Header("Ports Configuration", order = 20)]
28 | public int WebSocketPort;
29 | public int StatisticsPort;
30 | public int UDPPort;
31 | }
32 |
33 | public class Configs
34 | {
35 | public static ClientSettings clientSettings = Resources.Load("ClientSettings");
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/ObservableView.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using UnityEngine;
19 |
20 | namespace EdgeMultiplay
21 | {
22 | ///
23 | /// ObservableView is added by default to Observered objects,holding the reference to the observable owner and the observable index in in the observer.observable list
24 | ///
25 | [AddComponentMenu("EdgeMultiplay/ObservableView")]
26 | public class ObservableView : MonoBehaviour
27 | {
28 | public string ownerId;
29 | public int observableIndex; //the observable index in in the observer.observable list
30 |
31 | public void SetupObservableView(string ownerId, int observableIndex)
32 | {
33 | this.ownerId = ownerId;
34 | this.observableIndex = observableIndex;
35 | }
36 |
37 | public bool OwnerIsLocalPlayer()
38 | {
39 | if (EdgeManager.localPlayer.playerId == ownerId)
40 | {
41 | return true;
42 | }
43 | else
44 | {
45 | return false;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Logo/MobiledgeX_Icon_Green_Black.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f6328cde825c8425190e0ad08c7b3602
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 10
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 1
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: -1
38 | wrapV: -1
39 | wrapW: -1
40 | nPOTScale: 1
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 0
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 0
53 | spriteTessellationDetail: -1
54 | textureType: 0
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | platformSettings:
61 | - serializedVersion: 3
62 | buildTarget: DefaultTexturePlatform
63 | maxTextureSize: 2048
64 | resizeAlgorithm: 0
65 | textureFormat: -1
66 | textureCompression: 1
67 | compressionQuality: 50
68 | crunchedCompression: 0
69 | allowsAlphaSplitting: 0
70 | overridden: 0
71 | androidETC2FallbackOverride: 0
72 | forceMaximumCompressionQuality_BC6H_BC7: 0
73 | spriteSheet:
74 | serializedVersion: 2
75 | sprites: []
76 | outline: []
77 | physicsShape: []
78 | bones: []
79 | spriteID:
80 | internalID: 0
81 | vertices: []
82 | indices:
83 | edges: []
84 | weights: []
85 | secondaryTextures: []
86 | spritePackingTag:
87 | pSDRemoveMatte: 0
88 | pSDShowRemoveMatteOption: 0
89 | userData:
90 | assetBundleName:
91 | assetBundleVariant:
92 |
--------------------------------------------------------------------------------
/EdgeMultiplay/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this EdgeMultiplay Unity Client will be documented in this file.
3 |
4 | ## [1.3.0] - 2021-11-13
5 |
6 | ### Fixes & Improvements:
7 | - Proper error logs for calling EdgeManager functions before OnGameStart() callback.
8 |
9 | ### New Features:
10 | - EdgeManager.CreateRoom() now have a new argument minPlayersToStartGame (The minimum players threshold to start a game, new players will be able to join the running game as long as the room members count is below maxPlayersPerRoom threshold.
11 | - New ClientSettings menu, where you can set your ports and configure your host address.(Located under EdgeMultiplay/ClientSettings)
12 | - New Server Event rooms-updated, use OnRoomsUpdated() callback to get the latest status of rooms in the lobby.
13 |
14 |
15 | ## [1.2.0] - 2021-05-05
16 |
17 | ### Fixes & Improvements:
18 | - Fixed App Quiting error on PC/Mac Unity Player.
19 | - Fixed customizable fallback location for Oculus/Non Phone devices in your GameManager.cs
20 | ```
21 | EdgeManager.integration.useFallbackLocation = true;
22 | EdgeManager.integration.setFallbackLocation(longitude, latitude);
23 | ConnectToEdge(useFallBackLocation:true);
24 | ```
25 | - Use SendGamePlayEvent() and SendGamePlayEventUDP() in your PlayerManager to SendGamePlayEvents.
26 |
27 | ### New Features:
28 | - Server Stats : View analytics of an Edge Multiplay instance in a web browser. To view the closest instance's stats, click on the menu button Server Stats under the EdgeMultiplay menu in the Unity Editor.
29 | - New Ownership Transfer options : TakeOver and RequestOwnership.
30 | - UpdateRate (Fixed Update or EveryFrame) for EdgeMultiplayObserver.
31 | - New Example Scenes added (Chat Rooms, OwnershipExamples)
32 | - New notification callback added OnRoomRemovedFromLobby()
33 |
34 |
35 | ## [1.1.0] - 2021-04-01
36 |
37 | ### Fix & Improvements.
38 | - Custom Tags (App Dependent Tags) added to Players.
39 | - Observables ownership transfer, Creating observables at runtime.
40 | - Added Compatibility with Oculus SDK.
41 | - OrphanObservable added, Add OrphanObservable Component to GameObjects that don't have an owner at the time of creation (Ex. created in Unity Editor).
42 | - Example Scenes added (Creating synced objects at runtime, ownership transfer)
43 |
44 |
45 | ## [1.0.0] - 2021-02-12
46 |
47 | ### Fix & Improvements.
48 | - First release for EdgeMultiplay Unity Client
49 | - Getting started tutorials added https://www.youtube.com/watch?v=9kMz6Q3g0xQ&list=PLwUZZfaECSv18E5d0ooDR7S8416pImW8W
50 | - Example Scenes added (ChatExample, PingPongExample, ARPingPongExample)
51 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Editor/EdgeMultiplayEditorWindow.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using System.IO;
19 | using System.Threading.Tasks;
20 | using UnityEditor;
21 | using UnityEngine;
22 | using MobiledgeX;
23 | using DistributedMatchEngine;
24 |
25 | namespace EdgeMultiplay
26 | {
27 | [ExecuteInEditMode]
28 | public class EdgeMultiplayEditorWindow : EditorWindow
29 | {
30 |
31 | #region EdgeMultiplay ToolBar Menu items
32 |
33 | [MenuItem("EdgeMultiplay/Getting Started", false, 0)]
34 | public static void OpenGettingStartedURL()
35 | {
36 | Application.OpenURL("https://www.youtube.com/playlist?list=PLwUZZfaECSv18E5d0ooDR7S8416pImW8W");
37 | }
38 |
39 | [MenuItem("EdgeMultiplay/Examples/PingPong", false, 40)]
40 | public static void ImportPingPongExample()
41 | {
42 | string assetsFolder = Path.GetFullPath(Application.dataPath);
43 | AssetDatabase.ImportPackage(Path.Combine(assetsFolder, "EdgeMultiplay/Examples/PingPongExample.unitypackage"), true);
44 | }
45 |
46 | [MenuItem("EdgeMultiplay/Examples/ChatRooms", false, 40)]
47 | public static void ImportChatExample()
48 | {
49 | string assetsFolder = Path.GetFullPath(Application.dataPath);
50 | AssetDatabase.ImportPackage(Path.Combine(assetsFolder, "EdgeMultiplay/Examples/ChatRooms.unitypackage"), true);
51 | }
52 |
53 | [MenuItem("EdgeMultiplay/Examples/OwnershipExamples", false, 40)]
54 | public static void ImportOwnershipExamples()
55 | {
56 | string assetsFolder = Path.GetFullPath(Application.dataPath);
57 | AssetDatabase.ImportPackage(Path.Combine(assetsFolder, "EdgeMultiplay/Examples/OwnershipExamples.unitypackage"), true);
58 | }
59 |
60 | [MenuItem("EdgeMultiplay/Docs/How It Works?", false, 20)]
61 | public static void OpenHowItWorksURL()
62 | {
63 | Application.OpenURL("https://mobiledgex.github.io/edge-multiplay-unity-client/how_it_works.html");
64 | }
65 |
66 | [MenuItem("EdgeMultiplay/Docs/Documentation", false, 20)]
67 | public static void OpenDocumentationURL()
68 | {
69 | Application.OpenURL("https://mobiledgex.github.io/edge-multiplay-unity-client/");
70 | }
71 |
72 | [MenuItem("EdgeMultiplay/Join the Community", false, 20)]
73 | public static void JoinTheCommunity()
74 | {
75 | Application.OpenURL("https://discord.gg/CHCWfgrxh6");
76 | }
77 |
78 | [MenuItem("EdgeMultiplay/Report a bug", false, 60)]
79 | public static void ReportBug()
80 | {
81 | Application.OpenURL("https://github.com/mobiledgex/edge-multiplay-unity-client/issues/new/choose");
82 | }
83 |
84 | [MenuItem("EdgeMultiplay/ClientSettings", false, 0)]
85 | public static void ShowSettings()
86 | {
87 | Selection.objects = new Object[] { Configs.clientSettings };
88 | EditorGUIUtility.PingObject(Configs.clientSettings);
89 | }
90 |
91 | [MenuItem("EdgeMultiplay/Server Stats", false, 80)]
92 | public static async void ServerStats()
93 | {
94 | if (EditorUtility.DisplayDialog("EdgeMultiplay", "Where are you are running the server?", "Locally", "MobiledgeX"))
95 | {
96 | Application.OpenURL("http://localhost:" + Configs.clientSettings.StatisticsPort);
97 | }
98 | else
99 | {
100 | string fqdn = await GetMobiledgeXAppFQDN();
101 | if (fqdn == "")
102 | {
103 | Debug.LogError("Make sure MobiledgeX Settings AppDefs and Region are correct, Check the console for more details.");
104 | }
105 | else
106 | {
107 | Application.OpenURL("http://" + fqdn + ":" + Configs.clientSettings.StatisticsPort);
108 | }
109 | }
110 | }
111 |
112 | [MenuItem("EdgeMultiplay/Version 1.3", false, 80)]
113 | public static void Version()
114 | {
115 | //placeholder for version number
116 | }
117 |
118 | #endregion
119 |
120 | #region Helper functions
121 |
122 | public static async Task GetMobiledgeXAppFQDN()
123 | {
124 | MobiledgeXIntegration integration = new MobiledgeXIntegration();
125 | try
126 | {
127 | await integration.RegisterAndFindCloudlet();
128 | Debug.Log("FindCloudletReply.status = " + integration.FindCloudletReply.status);
129 | if (integration.FindCloudletReply.status == FindCloudletReply.FindStatus.FIND_FOUND)
130 | {
131 | return integration.FindCloudletReply.fqdn;
132 | }
133 | else
134 | {
135 | return "";
136 | }
137 | }
138 |
139 | //RegisterClientException is thrown if your app is not found
140 | catch (RegisterClientException rce)
141 | {
142 | Debug.LogError("RegisterClientException: " + rce.Message + "Inner Exception: " + rce.InnerException);
143 | return "";
144 | }
145 |
146 | //FindCloudletException is thrown if there is no app instance in the selected region
147 | catch (FindCloudletException fce)
148 | {
149 | Debug.LogError("FindCloudletException: " + fce.Message + "Inner Exception: " + fce.InnerException);
150 | return "";
151 | }
152 | }
153 | }
154 | }
155 |
156 | #endregion
157 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Editor/TemplateGenerator.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using UnityEditor;
19 | using UnityEngine;
20 | using System;
21 | using System.IO;
22 | namespace EdgeMultiplay
23 | {
24 | public class TemplateGenerator
25 | {
26 | [MenuItem("Assets/Create/EdgeMultiplay/GameManager", false, 1)]
27 | static void CreateGameManager()
28 | {
29 | string fileLocation = GetDirectoryPath() + "/GameManager.cs";
30 | if (File.Exists(fileLocation) == false)
31 | {
32 | using (StreamWriter outfile =
33 | new StreamWriter(fileLocation))
34 | {
35 | outfile.WriteLine("using UnityEngine;");
36 | outfile.WriteLine("using EdgeMultiplay;");
37 | outfile.WriteLine(" ");
38 | outfile.WriteLine("[RequireComponent(typeof(EdgeManager))]");
39 | outfile.WriteLine("public class GameManager : EdgeMultiplayCallbacks {");
40 | outfile.WriteLine(" ");
41 | outfile.WriteLine(" ");
42 | outfile.WriteLine(" // Use this for initialization");
43 | outfile.WriteLine(" void Start () {");
44 | outfile.WriteLine(" ConnectToEdge();");
45 | outfile.WriteLine(" }");
46 | outfile.WriteLine(" ");
47 | outfile.WriteLine(" // Called once connected to your server deployed on Edge");
48 | outfile.WriteLine(" public override void OnConnectionToEdge(){");
49 | outfile.WriteLine(" print (\"Connected to server deployed on Edge\");");
50 | outfile.WriteLine(" }");
51 | outfile.WriteLine(" ");
52 | outfile.WriteLine(" // Called once the server registers the player right after the connection is established");
53 | outfile.WriteLine(" public override void OnRegisterEvent(){");
54 | outfile.WriteLine(" print (\"Game Session received from server\");");
55 | outfile.WriteLine(" EdgeManager.JoinOrCreateRoom(\"playerName\", 0, 2);");
56 | outfile.WriteLine(" }");
57 | outfile.WriteLine(" ");
58 | outfile.WriteLine(" // Called once the JoinRoom request succeeded ");
59 | outfile.WriteLine(" public override void OnRoomJoin(Room room){");
60 | outfile.WriteLine(" print (\"Joined room\");");
61 | outfile.WriteLine(" print (\"Maximum Players in the room :\"+ room.maxPlayersPerRoom); ");
62 | outfile.WriteLine(" print (\"Count of Players in the room :\"+ room.roomMembers.Count); ");
63 | outfile.WriteLine(" }");
64 | outfile.WriteLine(" ");
65 | outfile.WriteLine(" // Called once the CreateRoom request succeeded ");
66 | outfile.WriteLine(" public override void OnRoomCreated(Room room){");
67 | outfile.WriteLine(" print (\"Created a room\");");
68 | outfile.WriteLine(" print (\"Maximum Players in the room :\"+ room.maxPlayersPerRoom); ");
69 | outfile.WriteLine(" print (\"Count of Players in the room :\"+ room.roomMembers.Count); ");
70 | outfile.WriteLine(" }");
71 | outfile.WriteLine(" ");
72 | outfile.WriteLine(" // Called once the Game start on the server");
73 | outfile.WriteLine(" // The game starts on the server once the count of room members reachs the maximum players per room");
74 | outfile.WriteLine(" public override void OnGameStart(){");
75 | outfile.WriteLine(" print (\"Game Started\"); ");
76 | outfile.WriteLine(" }");
77 | outfile.WriteLine(" ");
78 | outfile.WriteLine("}");
79 | }
80 | }
81 | AssetDatabase.Refresh();
82 | }
83 |
84 | [MenuItem("Assets/Create/EdgeMultiplay/PlayerManager",false, 1)]
85 | static void CreatePlayerManager()
86 | {
87 | string fileLocation = GetDirectoryPath() + "/PlayerManager.cs";
88 | if (File.Exists(fileLocation) == false)
89 | {
90 | using (StreamWriter outfile =
91 | new StreamWriter(fileLocation))
92 | {
93 | outfile.WriteLine("using UnityEngine;");
94 | outfile.WriteLine("using EdgeMultiplay;");
95 | outfile.WriteLine(" ");
96 | outfile.WriteLine("public class PlayerManager : NetworkedPlayer {");
97 | outfile.WriteLine(" ");
98 | outfile.WriteLine(" // Use this for initialization");
99 | outfile.WriteLine(" void OnEnable () {");
100 | outfile.WriteLine(" ListenToMessages();");
101 | outfile.WriteLine(" }");
102 | outfile.WriteLine(" ");
103 | outfile.WriteLine(" // Once the GameObject is destroyed");
104 | outfile.WriteLine(" void OnDestroy () {");
105 | outfile.WriteLine(" StopListening();");
106 | outfile.WriteLine(" }");
107 | outfile.WriteLine(" ");
108 | outfile.WriteLine(" // Called once a GamePlay Event is received from the server");
109 | outfile.WriteLine(" public override void OnMessageReceived(GamePlayEvent gamePlayEvent){");
110 | outfile.WriteLine(" print (\"GamePlayEvent received from server, event name: \" + gamePlayEvent.eventName );");
111 | outfile.WriteLine(" }");
112 | outfile.WriteLine(" ");
113 | outfile.WriteLine("}");
114 | }
115 | }
116 | AssetDatabase.Refresh();
117 | }
118 |
119 | static string GetDirectoryPath()
120 | {
121 | string path = "";
122 | var obj = Selection.activeObject;
123 | if (obj == null) path = "Assets";
124 | else path = AssetDatabase.GetAssetPath(obj.GetInstanceID());
125 | if (path.Length > 0)
126 | {
127 | if (Directory.Exists(path))
128 | {
129 | return path;
130 | }
131 | else
132 | {
133 | string[] pathFolders = path.Split('/');
134 | path = "";
135 | for (int i = 0; i < pathFolders.Length - 1; i++)
136 | {
137 | if (i == 0)
138 | {
139 | path += pathFolders[i];
140 | }
141 | else
142 | {
143 | path += "/" + pathFolders[i];
144 | }
145 | }
146 | return path;
147 | }
148 |
149 | }
150 | else
151 | {
152 | throw new Exception("EdgeMultiplay: Can't create files outside the Assets folder");
153 | }
154 |
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/Util.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using System;
19 | using UnityEngine;
20 |
21 | namespace EdgeMultiplay
22 | {
23 | public class Util
24 | {
25 | ///
26 | /// Extracts position data as float array [3] from a transform component
27 | ///
28 | /// Transform Component
29 | /// float array of size 3 containing transform.position data
30 | public static float[] GetPositionData(Transform transformComponent)
31 | {
32 | return new float[3] { transformComponent.position.x, transformComponent.position.y, transformComponent.position.z };
33 | }
34 |
35 | ///
36 | /// Extracts rotation euler's angles as float array [3] from a transform component
37 | ///
38 | /// Transform Component
39 | /// float array of size 3 containing transform.rotation.eulerAngles data
40 | public static float[] GetRotationEulerData(Transform transformComponent)
41 | {
42 | return new float[3] { transformComponent.rotation.eulerAngles.x, transformComponent.rotation.eulerAngles.y, transformComponent.rotation.eulerAngles.z };
43 | }
44 |
45 | ///
46 | /// Extracts position and rotation euler's angles as float array [6] from a transform component
47 | ///
48 | /// Transform Component
49 | /// float array of size 6 containing transform.position data[3] and transform.rotation.eulerAngles[3] data respectively
50 | public static float[] GetPositionAndRotationData(Transform transformComponent)
51 | {
52 | return new float[6] {transformComponent.position.x, transformComponent.position.y, transformComponent.position.z,
53 | transformComponent.rotation.eulerAngles.x, transformComponent.rotation.eulerAngles.y, transformComponent.rotation.eulerAngles.z };
54 | }
55 |
56 | ///
57 | /// Extracts local position data as float array [3] from a transform component
58 | ///
59 | /// Transform Component
60 | /// float array of size 3 containing transform.localPosition data
61 | public static float[] GetLocalPositionData(Transform transformComponent)
62 | {
63 | return new float[3] { transformComponent.localPosition.x, transformComponent.localPosition.y, transformComponent.localPosition.z };
64 | }
65 |
66 | ///
67 | /// Extracts local rotation euler's angles as float array [3] from a transform component
68 | ///
69 | /// Transform Component
70 | /// float array of size 3 containing transform.localRotation data
71 | public static float[] GetLocalRotationData(Transform transformComponent)
72 | {
73 | return new float[3] { transformComponent.localRotation.eulerAngles.x, transformComponent.localRotation.eulerAngles.y, transformComponent.localRotation.eulerAngles.z };
74 | }
75 |
76 | ///
77 | /// Extracts local position and local rotation euler's angles as float array [6] from a transform component
78 | ///
79 | /// Transform Component
80 | /// float array of size 6 containing transform.localPosition data[3] and transform.localRotation.eulerAngles[3] data respectively
81 | public static float[] GetLocalPositionAndRotationData(Transform transformComponent)
82 | {
83 | return new float[6] {transformComponent.localPosition.x, transformComponent.localPosition.y, transformComponent.localPosition.z,
84 | transformComponent.localRotation.eulerAngles.x, transformComponent.localRotation.eulerAngles.y, transformComponent.localRotation.eulerAngles.z };
85 | }
86 |
87 | ///
88 | /// Sets the local position of the supplied transform component to the supplied targetLocalPositon
89 | ///
90 | /// Transform Component
91 | /// Vector3 object representing local position
92 | public static void SetLocalPostion(Transform transformComponent, Vector3 targetLocalPosition)
93 | {
94 | var tempPosition = transformComponent.localPosition;
95 | tempPosition.Set(targetLocalPosition.x, targetLocalPosition.y, targetLocalPosition.z);
96 | transformComponent.localPosition = tempPosition;
97 | }
98 |
99 | ///
100 | /// Sets the local rotation of the supplied transform component to the supplied targetLocalRotation euler's angles
101 | ///
102 | /// Transform Component
103 | /// Vector3 object representing local rotation euler's angles
104 | public static void SetLocalRotation(Transform transformComponent, Vector3 targetRotationEulers)
105 | {
106 | Quaternion tempRotation = transformComponent.localRotation;
107 | Quaternion rot = Quaternion.Euler(targetRotationEulers);
108 | tempRotation.Set(rot.x, rot.y, rot.z, rot.w);
109 | transformComponent.localRotation = tempRotation;
110 | }
111 |
112 | ///
113 | /// Converts a Vector3 to a float array of size 3
114 | ///
115 | /// Vector3 object
116 | /// float array of size 3
117 | public static float[] ConvertVector3ToFloatArray(Vector3 vector3)
118 | {
119 | return new float[3] { vector3.x, vector3.y, vector3.z };
120 | }
121 |
122 | ///
123 | /// Converts a quaternion to a float array of size 4 -> (x,y,z,w)
124 | ///
125 | /// Quaternion object
126 | /// float array of size 4
127 | public static float[] ConvertQuaternionToFloatArray(Quaternion quaternion)
128 | {
129 | return new float[4] { quaternion.x, quaternion.y, quaternion.z, quaternion.w };
130 | }
131 |
132 | ///
133 | /// Returns a Vector 3 from the supplied floatArray starting from the supplied startIndex, your floatArray size must be larger than 2
134 | ///
135 | /// the start index of the Vector3 in the float array
136 | /// Vector3 Object
137 | public static Vector3 ConvertFloatArrayToVector3(float[] floatArray, int startIndex = 0)
138 | {
139 | if (floatArray.Length > (startIndex + 2))
140 | {
141 | return new Vector3(floatArray[startIndex], floatArray[startIndex + 1], floatArray[startIndex + 2]);
142 | }
143 | else
144 | {
145 | throw new Exception("floatArray starting from the start index doesn't qualify to create a Vector3");
146 | }
147 | }
148 |
149 | ///
150 | /// Returns a Quaternion from the supplied floatArray starting from the supplied startIndex, your floatArray size must be larger than 3
151 | ///
152 | /// the start index of the Quaternion in the float array
153 | /// Quaternion Object
154 | public static Quaternion ConvertFloatArrayToQuaternion(float[] floatArray, int startIndex = 0)
155 | {
156 | if (floatArray.Length > (startIndex + 2))
157 | {
158 | return new Quaternion(floatArray[startIndex], floatArray[startIndex + 1], floatArray[startIndex + 2], floatArray[startIndex + 3]);
159 | }
160 | else
161 | {
162 | throw new Exception("floatArray starting from the start index doesn't qualify to create a Vector3");
163 | }
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/EdgeMultiplayCallbacks.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using UnityEngine;
19 | using System;
20 | using System.Collections;
21 | using System.Collections.Generic;
22 |
23 | namespace EdgeMultiplay
24 | {
25 | public class EdgeMultiplayCallbacks : MonoBehaviour
26 | {
27 | public static Action connectedToEdge;
28 | public static Action failureToConnect;
29 | public static Action registerEvent;
30 | public static Action notificationEvent;
31 | public static Action eventReceived;
32 | public static Action udpEventReceived;
33 | public static Action playerLeft;
34 | public static Action> roomsList;
35 | public static Action roomCreated;
36 | public static Action roomJoin;
37 | public static Action playerRoomJoined;
38 | public static Action joinRoomFaliure;
39 | public static Action newRoomCreatedInLobby;
40 | public static Action roomRemovedFromLobby;
41 | public static Action roomsUpdated;
42 | public static Action leftRoom;
43 | public static Action gameStart;
44 | public static Action gameEnd;
45 | public static Action newObservableCreated;
46 |
47 | ///
48 | /// Starts the connection to your Edge server, server discovery is based on GPS location and the telecommunication carrier
49 | ///
50 | /// True by default, set to false to connect to a specific carrier, set carrier name using EdgeManager.integration.carrierName
51 | /// False by default, location is acquired from user GPS location, if you are using location blind device like Oculus, use EdgeManager.integration.SetFallbackLocation()
52 | /// You can specify a path for your connection to be verified on the server side
53 | public void ConnectToEdge(bool useAnyCarrierNetwork = true, bool useFallBackLocation = false, string path = "")
54 | {
55 | connectedToEdge += OnConnectionToEdge;
56 | failureToConnect += OnFaliureToConnect;
57 | registerEvent += OnRegisterEvent;
58 | notificationEvent += OnNotificationEvent;
59 | roomsList += OnRoomsListReceived;
60 | newRoomCreatedInLobby += OnNewRoomCreatedInLobby;
61 | roomRemovedFromLobby += OnRoomRemovedFromLobby;
62 | roomCreated += OnRoomCreated;
63 | roomJoin += OnRoomJoin;
64 | playerRoomJoined += PlayerJoinedRoom;
65 | joinRoomFaliure += OnJoinRoomFailed;
66 | gameStart += OnGameStart;
67 | roomsUpdated += OnRoomsUpdated;
68 | playerLeft += OnPlayerLeft;
69 | leftRoom += OnLeftRoom;
70 | eventReceived += OnWebSocketEventReceived;
71 | udpEventReceived += OnUDPEventReceived;
72 | newObservableCreated += OnNewObservableCreated;
73 | StartCoroutine(ConnectToEdgeCoroutine(useAnyCarrierNetwork, useFallBackLocation, path));
74 | }
75 |
76 | IEnumerator ConnectToEdgeCoroutine(bool useAnyCarrierNetwork = true, bool useFallBackLocation = false, string path = "")
77 | {
78 | EdgeManager edgeManager = FindObjectOfType();
79 | if (Configs.clientSettings.useLocalHostServer == false)
80 | {
81 | if (SystemInfo.supportsLocationService)
82 | {
83 | yield return StartCoroutine(MobiledgeX.LocationService.EnsureLocation());
84 | }
85 | else
86 | {
87 | useFallBackLocation = true;
88 | yield return null;
89 | }
90 | }
91 | edgeManager.ConnectToServer(useAnyCarrierNetwork, useFallBackLocation, path).ConfigureAwait(false);
92 | }
93 |
94 | ///
95 | /// Called once the ConnectToEdge Request succeed
96 | ///
97 | public virtual void OnConnectionToEdge()
98 | {
99 | Debug.Log("Connected to Edge");
100 | }
101 |
102 | ///
103 | /// Called once the ConnectToEdge Request fails
104 | ///
105 | /// Reason of connection faliure
106 | public virtual void OnFaliureToConnect(string reason)
107 | {
108 | Debug.Log("Edge Connection Falied because " + reason);
109 | }
110 |
111 | ///
112 | /// Called once the server assigned a playerId to the user right after the connection is established
113 | ///
114 | public virtual void OnRegisterEvent()
115 | {
116 | Debug.Log("Register Event From Server");
117 | }
118 |
119 | ///
120 | /// Called once a notification is received from the server
121 | ///
122 | /// the notification received from the server
123 | public virtual void OnNotificationEvent(Notification notification)
124 | {
125 | Debug.Log("Notification Event From Server :" + notification.notificationText);
126 | }
127 |
128 | ///
129 | /// The response for EdgeManager.GetRooms()
130 | /// returns list of rooms on the server
131 | ///
132 | /// List of the rooms on the server
133 | public virtual void OnRoomsListReceived(List rooms)
134 | {
135 | Debug.Log("Rooms List Received from the server");
136 | }
137 |
138 | ///
139 | /// Called automatically once a new room is created on the server
140 | /// You can use this callback to call EdgeManager.GetRooms() to get the updated list of the rooms in the lobby
141 | ///
142 | public virtual void OnNewRoomCreatedInLobby()
143 | {
144 | Debug.Log("New Room Created In the Lobby, Call EdgeManager.GetRooms() to get the updated rooms list");
145 | }
146 |
147 | ///
148 | /// Called automatically once a room is removed from the lobby
149 | /// You can use this callback to call EdgeManager.GetRooms() to get the updated list of the rooms in the lobby
150 | ///
151 | public virtual void OnRoomRemovedFromLobby()
152 | {
153 | Debug.Log("A Room have been removed from the Lobby, Call EdgeManager.GetRooms() to get the updated rooms list");
154 | }
155 |
156 | ///
157 | /// Called once the local player creates a room,
158 | /// The response of EdgeManagre.CreateRoom() and might be the response of EdgeManager.JoinOrCreateRoom() in case of room creation
159 | ///
160 | /// Created Room Info
161 | public virtual void OnRoomCreated(Room room)
162 | {
163 | Debug.Log("Room Created Event From Server");
164 | }
165 |
166 | ///
167 | /// Called once the local player joins a room,
168 | /// The response of EdgeManagre.JoinRoom() and might be the response of EdgeManager.JoinOrCreateRoom() in case of room join
169 | ///
170 | /// Joined Room Info
171 | public virtual void OnRoomJoin(Room room)
172 | {
173 | Debug.Log("Room Join Event From Server");
174 | }
175 |
176 | ///
177 | /// Called automatically once a player joins the local player room
178 | ///
179 | /// Updated Room Info
180 | public virtual void PlayerJoinedRoom(Room room)
181 | {
182 | Debug.Log("PlayerJoinedRoom Event From Server");
183 | }
184 |
185 | ///
186 | /// Called once a JoinRoom Request fails on the server
187 | ///
188 | public virtual void OnJoinRoomFailed()
189 | {
190 | Debug.Log("Join Room Failed Event From Server");
191 | }
192 |
193 | ///
194 | /// Called once the game starts,
195 | /// the game starts on the server once the number of players == the maximum players per room
196 | ///
197 | public virtual void OnGameStart()
198 | {
199 | Debug.Log("Game Start Event From Server");
200 | }
201 |
202 | ///
203 | /// Called once a player in the same room as the local player leaves the room
204 | /// If the player who left was tracking any transforms
205 | /// this callback will be where you should transfer observables ownership to another player if the game is still running.
206 | ///
207 | /// Info about the player who left the room
208 | public virtual void OnPlayerLeft(RoomMemberLeft playerLeft)
209 | {
210 | Debug.Log("Player Left Event From Server");
211 | }
212 |
213 | ///
214 | /// Called once a player joins or leaves a room in the lobby
215 | /// this callback can be useful to update rooms status, Call EdgeManager.GetRooms() to get the latest rooms updates
216 | ///
217 | public virtual void OnRoomsUpdated()
218 | {
219 | Debug.Log("RoomsUpdate Event From Server");
220 | }
221 |
222 | ///
223 | /// Called once ExitRoom request in(EdgeManager.ExitRoom()) succeed
224 | ///
225 | public virtual void OnLeftRoom()
226 | {
227 | Debug.Log("Left Room Event From Server");
228 | }
229 |
230 | ///
231 | /// During GamePlay once a event received from another player (Websocket)
232 | ///
233 | /// received GamePlayEvent
234 | public virtual void OnWebSocketEventReceived(GamePlayEvent gamePlayEvent)
235 | {
236 | Debug.Log("WebSocket Event Received From Server : " + gamePlayEvent.eventName);
237 | }
238 |
239 | ///
240 | /// Server Callback when a new Observable is created by one of the players
241 | ///
242 | /// The created Observable object
243 | public virtual void OnNewObservableCreated(Observable observable)
244 | {
245 | Debug.Log("New Observable created, owner name : " + observable.owner.playerName);
246 | }
247 |
248 | ///
249 | /// During GamePlay once a event received from another player (UDP)
250 | ///
251 | /// received GamePlayEvent
252 | public virtual void OnUDPEventReceived(GamePlayEvent gamePlayEvent)
253 | {
254 | //Debug.Log("UDP Msg Received Event From Server : " + gamePlayEvent.eventName);
255 | }
256 |
257 | private void OnDestroy()
258 | {
259 | connectedToEdge -= OnConnectionToEdge;
260 | failureToConnect -= OnFaliureToConnect;
261 | registerEvent -= OnRegisterEvent;
262 | notificationEvent -= OnNotificationEvent;
263 | leftRoom += OnLeftRoom;
264 | roomsList -= OnRoomsListReceived;
265 | newRoomCreatedInLobby -= OnNewRoomCreatedInLobby;
266 | roomRemovedFromLobby -= OnRoomRemovedFromLobby;
267 | roomCreated -= OnRoomCreated;
268 | roomJoin -= OnRoomJoin;
269 | playerRoomJoined -= PlayerJoinedRoom;
270 | joinRoomFaliure -= OnJoinRoomFailed;
271 | gameStart -= OnGameStart;
272 | playerLeft -= OnPlayerLeft;
273 | roomsUpdated -= OnRoomsUpdated;
274 | eventReceived -= OnWebSocketEventReceived;
275 | udpEventReceived -= OnUDPEventReceived;
276 | newObservableCreated -= OnNewObservableCreated;
277 | }
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/EdgeMultiplayObserver.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using UnityEngine;
21 |
22 |
23 | namespace EdgeMultiplay
24 | {
25 | ///
26 | /// The rate which the observer checks if the observed objects' transform data have been updated
27 | ///
28 | public enum UpdateRate
29 | {
30 | EveryFrame,
31 | FixedUpdate
32 | }
33 |
34 | ///
35 | /// EdgeMultiplayObserver can be added to on an object to sync its position and/or rotation between all players
36 | /// You can sync a PlayerObject or Non PlayerObject
37 | ///
38 | [AddComponentMenu("EdgeMultiplay/EdgeMultiplayObserver")]
39 | public class EdgeMultiplayObserver : MonoBehaviour
40 | {
41 | #region EdgeMultiplayObserver Variables
42 | ///
43 | [HideInInspector]
44 | public NetworkedPlayer networkedPlayer;
45 |
46 | ///
47 | /// The rate which the observer checks if the observed objects' transform data have been updated
48 | /// Default is EveryFrame (Update)
49 | /// If you see the observed Game Object movement stutters use FixedUpdate
50 | ///
51 | public UpdateRate updateRate;
52 |
53 | ///
54 | /// List of observed objects
55 | ///
56 | [Tooltip("Add all sync Transforms including the player transform")]
57 | public List observables;
58 |
59 | #endregion
60 |
61 | #region MonoBehaviour Callbacks
62 |
63 | private void Awake()
64 | {
65 | EdgeManager.observers.Add(this);
66 | networkedPlayer = GetComponent();
67 | UpdateObservables();
68 | }
69 |
70 | private void OnValidate()
71 | {
72 | if (!GetComponent())
73 | {
74 | Debug.LogWarning("EdgeMultiplay: EdgeMultiplayObserver requires PlayerManager or a class that inherits from NetworkedPlayer," +
75 | "\nRemove EdgeMultiplayObserver Component from " + gameObject.name);
76 | }
77 | if (observables.Count > 0)
78 | {
79 | for (int i = 0; i < observables.Count; i++)
80 | {
81 | observables[i].SetObservableIndex(i);
82 | if (observables[i].observeredTransform)
83 | {
84 | observables[i].SetupObservable(networkedPlayer);
85 | }
86 | }
87 | }
88 | }
89 |
90 | private void FixedUpdate()
91 | {
92 | if (updateRate == UpdateRate.FixedUpdate)
93 | {
94 | if (EdgeManager.gameStarted)
95 | {
96 | foreach (Observable observable in observables)
97 | {
98 | if (RequiresUpdate(observable))
99 | {
100 | if (networkedPlayer && networkedPlayer.isLocalPlayer)
101 | {
102 | observable.SendDataToServer();
103 | }
104 | }
105 | }
106 | }
107 | }
108 | }
109 |
110 | private void Update()
111 | {
112 | if (updateRate == UpdateRate.EveryFrame)
113 | {
114 | if (EdgeManager.gameStarted)
115 | {
116 | foreach (Observable observable in observables)
117 | {
118 | if (RequiresUpdate(observable))
119 | {
120 | if (networkedPlayer && networkedPlayer.isLocalPlayer)
121 | {
122 | observable.SendDataToServer();
123 | }
124 | }
125 | }
126 | }
127 | }
128 | }
129 |
130 | #endregion
131 |
132 | #region EdgeMultiplayObserver Functions
133 | ///
134 | ///UpdateObservers does the following:
135 | /// 1.Update the observables list indices.
136 | /// 2.Updates/Assigns an Obserview View to the observed game objects.
137 | /// 3.Disables the rigid body 2d or 3d on the observed game objects, so the physics can be simulated from the owner only.
138 | /// You should call UpdateObservables() at these situations:
139 | /// 1.New observable added.
140 | /// 2.Observable ownership change.
141 | ///
142 | public void UpdateObservables()
143 | {
144 | for (int i = 0; i < observables.Count; i++)
145 | {
146 | ObservableView observerView;
147 | observables[i].SetObservableIndex(i);
148 | if (observables[i].observeredTransform.gameObject.GetComponent())
149 | {
150 | observerView = observables[i].observeredTransform.gameObject.GetComponent();
151 | }
152 | else
153 | {
154 | observerView = observables[i].observeredTransform.gameObject.AddComponent();
155 | }
156 | observerView.SetupObservableView(networkedPlayer.playerId, i);
157 |
158 | if (observables[i].observeredTransform != null)
159 | {
160 | observables[i].SetupObservable(networkedPlayer);
161 | }
162 | if (networkedPlayer && !networkedPlayer.isLocalPlayer && observables[i].owner == null)
163 | {
164 | if (observables[i].observeredTransform && observables[i].observeredTransform.GetComponent())
165 | {
166 | observables[i].observeredTransform.GetComponent().isKinematic = true;
167 | }
168 | if (observables[i].observeredTransform && observables[i].observeredTransform.GetComponent())
169 | {
170 | observables[i].observeredTransform.GetComponent().isKinematic = true;
171 | }
172 | }
173 | else
174 | {
175 | if (observables[i].owner != null && !observables[i].owner.isLocalPlayer && observables[i].attachedToPlayer)
176 | {
177 | if (observables[i].observeredTransform.GetComponent())
178 | {
179 | observables[i].observeredTransform.GetComponent().isKinematic = true;
180 | }
181 | if (observables[i].observeredTransform.GetComponent())
182 | {
183 | observables[i].observeredTransform.GetComponent().isKinematic = true;
184 | }
185 | }
186 | else
187 | {
188 | if (observables[i].owner != null && observables[i].owner.isLocalPlayer && observables[i].attachedToPlayer)
189 | {
190 | if (observables[i].observeredTransform.GetComponent())
191 | {
192 | observables[i].observeredTransform.GetComponent().isKinematic = false;
193 | }
194 | if (observables[i].observeredTransform.GetComponent())
195 | {
196 | observables[i].observeredTransform.GetComponent().isKinematic = false;
197 | }
198 | }
199 | }
200 | }
201 | }
202 | }
203 |
204 | private bool RequiresUpdate(Observable observable)
205 | {
206 | switch (observable.syncOption)
207 | {
208 | case SyncOptions.SyncPosition:
209 | if (observable.lastPosition != observable.observeredTransform.position)
210 | return true;
211 | else
212 | return false;
213 | case SyncOptions.SyncRotation:
214 | if (observable.lastRotation != observable.observeredTransform.rotation.eulerAngles)
215 | return true;
216 | else
217 | return false;
218 | default:
219 | case SyncOptions.SyncPositionAndRotation:
220 | if (observable.lastPosition != observable.observeredTransform.position || observable.lastRotation != observable.observeredTransform.rotation.eulerAngles)
221 | return true;
222 | else
223 | return false;
224 | }
225 | }
226 |
227 | ///
228 | /// Call TakeOverObservable() to take over the ownership of an observable you don't own
229 | /// For TakeOverObservable() to work, the observable must have the squattingAllowed boolean sat to true
230 | ///
231 | /// The ObservableView component attached to the Synced GameObject
232 | ///
233 | /// All Synced GameObjects have ObservableView component added at runtime
234 | ///
235 | ///
236 | public void TakeOverObservable(ObservableView observableView)
237 | {
238 | EdgeMultiplayObserver currentOwner = EdgeManager.observers.Find(observer => observer.networkedPlayer.playerId == observableView.ownerId);
239 | if (currentOwner == null)
240 | {
241 | Debug.LogWarning("EdgeMultiplay: Couldn't find the owner");
242 | return;
243 | }
244 |
245 | if (currentOwner.networkedPlayer.playerId == networkedPlayer.playerId)
246 | {
247 | Debug.LogWarning("EdgeMultiplay: No ownership change needed you already own the observable object");
248 | return;
249 | }
250 |
251 | Observable observable = currentOwner.observables.Find(obs => obs.observableIndex == observableView.observableIndex);
252 |
253 | if (observable == null)
254 | {
255 | Debug.LogWarning("EdgeMultiplay: Couldn't find the observable");
256 | return;
257 | }
258 |
259 | if (!observable.squattingAllowed)
260 | {
261 | Debug.LogWarning("EdgeMultiplay: Couldn't TakeOver Observable, Squatting is not Allowed");
262 | return;
263 | }
264 | else
265 | {
266 | EdgeManager.MessageSender.SendGamePlayEvent(new GamePlayEvent()
267 | {
268 | eventName = "TakeOverObservable",
269 | stringData = new string[1] { observableView.ownerId },
270 | integerData = new int[1] { observableView.observableIndex }
271 | });
272 | }
273 | }
274 |
275 | ///
276 | /// Call RequestOwnership() to request the ownership transfer of an observable from its owner
277 | /// It's up to the owner to approve or reject the ownership transfer
278 | /// This will trigger OnOwnershipRequestReceived() callback on the observable owner
279 | ///
280 | /// The ObservableView component attached to the Synced GameObject
281 | ///
282 | /// All Synced GameObjects have ObservableView component added at runtime
283 | ///
284 | ///
285 | public void RequestOwnership(ObservableView observableView)
286 | {
287 | EdgeMultiplayObserver currentOwner =
288 | EdgeManager.observers.Find(observer => observer.networkedPlayer.playerId == observableView.ownerId);
289 |
290 | if (currentOwner == null)
291 | {
292 | Debug.LogWarning("EdgeMultiplay: Couldn't find the owner");
293 | return;
294 | }
295 |
296 | if (currentOwner.networkedPlayer.playerId == networkedPlayer.playerId)
297 | {
298 | Debug.LogWarning("EdgeMultiplay: No ownership change needed you already own the observable object");
299 | return;
300 | }
301 |
302 | Observable observable = currentOwner.observables
303 | .Find(obs => obs.observableIndex == observableView.observableIndex);
304 |
305 | if (observable == null)
306 | {
307 | Debug.LogWarning("EdgeMultiplay: Couldn't find the observable");
308 | return;
309 | }
310 |
311 | EdgeManager.MessageSender.SendGamePlayEvent(new GamePlayEvent()
312 | {
313 | eventName = "OwnershipRequest",
314 | stringData = new string[1] { observableView.ownerId },
315 | integerData = new int[1] { observableView.observableIndex }
316 | });
317 |
318 | }
319 | #endregion
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/NetworkedPlayer.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using UnityEngine;
21 |
22 | namespace EdgeMultiplay
23 | {
24 | ///
25 | /// The player script should inherit from Networked Player
26 | /// NetworkedPlayer contains all the player data retrieved from the server
27 | ///
28 | [AddComponentMenu("EdgeMultiplay/NetworkedPlayer")]
29 | public class NetworkedPlayer : EdgeMultiplayCallbacks
30 | {
31 | #region NetworkedPlayer Variables
32 |
33 | ///
34 | /// the id of the room that the player is a member of
35 | ///
36 | public string roomId;
37 | ///
38 | /// the player order in the room, ex. the first player to join a room has playerIndex of 0
39 | ///
40 | public int playerIndex;
41 | ///
42 | /// The player Id that the server assigns to the player
43 | ///
44 | public string playerId;
45 | public string playerName;
46 | public Action ownershipRequested;
47 | public Action playerEvent;
48 | public bool isLocalPlayer;
49 | ///
50 | /// Can be used to set the player to be active or inactive although the player is still an active connection in the room
51 | ///
52 | public bool ActivePlayer = true;
53 | EdgeManager edgeManager;
54 | ///
55 | /// The observer responsible for tracking the observable transforms
56 | ///
57 | public EdgeMultiplayObserver observer
58 | {
59 | get
60 | {
61 | if (GetComponent())
62 | {
63 | return GetComponent();
64 | }
65 | else
66 | {
67 | return null;
68 | }
69 | }
70 | set { }
71 | }
72 |
73 | #endregion
74 |
75 | #region NetworkedPlayer Functions
76 |
77 | private void Awake()
78 | {
79 | edgeManager = FindObjectOfType();
80 | }
81 |
82 | ///
83 | /// Call ListenToMessages() to start listening to messages from the server
84 | ///
85 | public void ListenToMessages()
86 | {
87 | if (!isLocalPlayer)
88 | {
89 | playerEvent += OnMessageReceived;
90 | }
91 | else
92 | {
93 | ownershipRequested += OnOwnershipRequestReceived;
94 | }
95 | }
96 | ///
97 | /// Add StopListening when you want to stop listening to messages from the server
98 | /// Recommended to have it under the Monobehaviour callback OnDestroy()
99 | ///
100 | public void StopListening()
101 | {
102 | if (!isLocalPlayer)
103 | {
104 | playerEvent -= OnMessageReceived;
105 | }
106 | else
107 | {
108 | ownershipRequested += OnOwnershipRequestReceived;
109 | }
110 | }
111 |
112 | public override void OnWebSocketEventReceived(GamePlayEvent gameplayEvent)
113 | {
114 | if (!isLocalPlayer)
115 | {
116 | if (gameplayEvent.senderId == playerId)
117 | {
118 | playerEvent(gameplayEvent);
119 | }
120 | }
121 | }
122 |
123 | public override void OnUDPEventReceived(GamePlayEvent udpEvent)
124 | {
125 | if (!isLocalPlayer)
126 | {
127 | if (udpEvent.senderId == playerId)
128 | {
129 | playerEvent(udpEvent);
130 | }
131 | }
132 | }
133 |
134 | ///
135 | /// Called once GamePlay Events Received from the server
136 | ///
137 | /// the received gamePlayEvent
138 | public virtual void OnMessageReceived(GamePlayEvent gamePlayEvent)
139 | {
140 | Debug.Log("GamePlayEvent Received");
141 | }
142 |
143 | ///
144 | /// OnOwnershipRequestReceived is Received from the server
145 | /// once another player requests the ownership of an observable
146 | ///
147 | /// The player who requested the ownership
148 | /// The observable which the request is about
149 | public virtual void OnOwnershipRequestReceived(NetworkedPlayer requestee, Observable observable)
150 | {
151 | Debug.Log("Ownership Request Received");//To Approve the transfer >> observable.ChangeOwnership(requestee.playerId);
152 | }
153 |
154 | public void SetUpPlayer(Player playerFromServer, string roomId, bool isLocalPlayer = false)
155 | {
156 | this.roomId = roomId;
157 | playerName = playerFromServer.playerName;
158 | playerIndex = playerFromServer.playerIndex;
159 | playerId = playerFromServer.playerId;
160 | this.isLocalPlayer = isLocalPlayer;
161 | ActivePlayer = true;
162 | }
163 |
164 | ///
165 | /// Sends an event to all players in the room
166 | ///
167 | /// The GamePlayEvent to send
168 | public void SendGamePlayEvent(GamePlayEvent gamePlayEvent)
169 | {
170 | GamePlayEvent gameplayEvent = new GamePlayEvent()
171 | {
172 | eventName = gamePlayEvent.eventName,
173 | booleanData = gamePlayEvent.booleanData,
174 | stringData = gamePlayEvent.stringData,
175 | integerData = gamePlayEvent.integerData,
176 | floatData = gamePlayEvent.floatData,
177 | };
178 | edgeManager.SendGamePlayEvent(gameplayEvent);
179 | }
180 |
181 | ///
182 | /// Sends an event to all players in the room (UDP)
183 | ///
184 | /// The GamePlayEvent to send
185 | public void SendGamePlayEventUDP(GamePlayEvent gamePlayEvent)
186 | {
187 | GamePlayEvent gameplayEvent = new GamePlayEvent()
188 | {
189 | eventName = gamePlayEvent.eventName,
190 | booleanData = gamePlayEvent.booleanData,
191 | stringData = gamePlayEvent.stringData,
192 | integerData = gamePlayEvent.integerData,
193 | floatData = gamePlayEvent.floatData,
194 | };
195 | EdgeManager.SendUDPMessage(gameplayEvent);
196 | }
197 |
198 | ///
199 | /// Sends an event to all players in the room except the sender
200 | ///
201 | public void BroadcastMessage(GamePlayEvent gamePlayEvent)
202 | {
203 | GamePlayEvent gameplayEvent = new GamePlayEvent()
204 | {
205 | eventName = gamePlayEvent.eventName,
206 | booleanData = gamePlayEvent.booleanData,
207 | stringData = gamePlayEvent.stringData,
208 | integerData = gamePlayEvent.integerData,
209 | floatData = gamePlayEvent.floatData,
210 | };
211 | edgeManager.SendGamePlayEvent(gameplayEvent);
212 | }
213 |
214 | ///
215 | /// Sends an event to all players in the room except the sender
216 | ///
217 | public void BroadcastMessage(string eventName, Vector3 position)
218 | {
219 | GamePlayEvent gameplayEvent = new GamePlayEvent(eventName, position);
220 | edgeManager.SendGamePlayEvent(gameplayEvent);
221 | }
222 |
223 | ///
224 | /// Sends an event to all players in the room except the sender
225 | ///
226 | public void BroadcastMessage(string eventName, Quaternion rotation)
227 | {
228 | GamePlayEvent gameplayEvent = new GamePlayEvent(eventName, rotation);
229 | edgeManager.SendGamePlayEvent(gameplayEvent);
230 | }
231 |
232 | ///
233 | /// Sends an event to all players in the room except the sender
234 | ///
235 | public void BroadcastMessage(string eventName, Vector3 position, Quaternion rotation)
236 | {
237 | GamePlayEvent gameplayEvent = new GamePlayEvent(eventName, position, rotation);
238 | edgeManager.SendGamePlayEvent(gameplayEvent);
239 | }
240 |
241 | ///
242 | /// Sends an event to all players in the room except the sender
243 | ///
244 | public void BroadcastMessage(string eventName, List integerArray)
245 | {
246 | GamePlayEvent gameplayEvent = new GamePlayEvent(eventName, integerArray);
247 | edgeManager.SendGamePlayEvent(gameplayEvent);
248 | }
249 |
250 | ///
251 | /// Sends an event to all players in the room except the sender
252 | ///
253 | public void BroadcastMessage(string eventName, List floatArray)
254 | {
255 | GamePlayEvent gameplayEvent = new GamePlayEvent(eventName, floatArray);
256 | edgeManager.SendGamePlayEvent(gameplayEvent);
257 | }
258 |
259 | ///
260 | /// Sends an event to all players in the room except the sender
261 | ///
262 | public void BroadcastMessage(string eventName, string[] stringData = null, int[] commandInts = null,
263 | float[] floatData = null,
264 | bool[] booleanData = null)
265 | {
266 | GamePlayEvent gamePlayEvent = new GamePlayEvent(this.roomId, playerId, eventName, stringData, commandInts, floatData, booleanData);
267 | edgeManager.SendGamePlayEvent(gamePlayEvent);
268 | }
269 |
270 | ///
271 | /// Creates synced GameObject in runtime in the local player's world
272 | /// and sends an event to all players in the room to create the observable in their worlds
273 | ///
274 | /// The name of your prefab (Game Object) stored in Resources Folder without extensions ex. 'Ball'
275 | /// the inital position of the spawning
276 | /// the inital rotation of the spawning
277 | /// Which Synchronization option will be applied
278 | /// Set to true if you want to smoothen the tracked position if you have network lag
279 | /// Set to true if you want to smoothen the tracked rotation if you have network lag
280 | /// Set Interpolation factor between 0.1 and 1
281 | /// The created Observer object
282 | public Observable CreateObservableObject(string prefabName, Vector3 startPosition, Quaternion startRotation, SyncOptions syncOption, bool interpolatePosition = false, bool interpolateRotation = false, float interpolationFactor = 0)
283 | {
284 | GameObject prefab = Resources.Load(prefabName);
285 | GameObject syncedObject;
286 | syncedObject = Instantiate(prefab, startPosition, startRotation);
287 | if (!observer)
288 | {
289 | observer = gameObject.AddComponent();
290 | }
291 | Observable newObservable = new Observable(syncedObject.transform, syncOption, interpolatePosition, interpolateRotation, Mathf.Clamp(interpolationFactor, 0.1f, 1f), observer.observables.Count);
292 | observer.observables.Add(newObservable);
293 | newObservable.SetupObservable(this);
294 | observer.UpdateObservables();
295 | if (isLocalPlayer)
296 | {
297 | GamePlayEvent newObserverEvent = new GamePlayEvent
298 | {
299 | eventName = "NewObservableCreated",
300 | booleanData = new bool[2] { interpolatePosition, interpolateRotation },
301 | stringData = new string[2] { playerId, prefabName },
302 | integerData = new int[1] { (int)syncOption },
303 | floatData = new float[7] { startPosition.x, startPosition.y, startPosition.z, startRotation.eulerAngles.x, startRotation.eulerAngles.y, startRotation.eulerAngles.z, interpolationFactor },
304 | };
305 | edgeManager.SendGamePlayEvent(newObserverEvent);
306 | }
307 | return newObservable;
308 | }
309 |
310 | #endregion
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/DataContracts.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using System;
19 | using System.Runtime.Serialization.Json;
20 | using System.IO;
21 | using System.Text;
22 | using System.Runtime.Serialization;
23 | using System.Collections.Generic;
24 | using UnityEngine;
25 | using System.Collections;
26 | using DistributedMatchEngine;
27 |
28 | namespace EdgeMultiplay
29 | {
30 | #region EdgeMultiplay Data Structures
31 |
32 | ///
33 | /// Synchronization Options
34 | ///
35 | public enum SyncOptions
36 | {
37 | SyncPosition,
38 | SyncRotation,
39 | SyncPositionAndRotation,
40 | SyncLocalPosition,
41 | SyncLocalRotation,
42 | SyncLocalPositionAndRotation
43 | }
44 |
45 | ///
46 | /// Called once a notification is received from the server
47 | /// Example of Notifications are new room created on server
48 | ///
49 | [DataContract]
50 | public class Notification
51 | {
52 | [DataMember]
53 | public string type = "notification";
54 |
55 | [DataMember]
56 | public string notificationText;
57 | }
58 |
59 | ///
60 | /// Register Event is being emitted from the server once the connection starts
61 | ///
62 | [DataContract]
63 | public class Register
64 | {
65 | [DataMember]
66 | public string type = "register";
67 |
68 | [DataMember]
69 | public string sessionId;
70 |
71 | [DataMember]
72 | public string playerId;
73 |
74 | }
75 |
76 | ///
77 | /// Indicates that server created a room but still waiting for more players to Join
78 | ///
79 | [DataContract]
80 | public class RoomCreated
81 | {
82 | [DataMember]
83 | public string type = "roomCreated";
84 | [DataMember]
85 | public Room room;
86 | }
87 |
88 |
89 | ///
90 | /// Indicates that player joined a room but still waiting for more players to Join
91 | ///
92 | [DataContract]
93 | public class RoomJoin
94 | {
95 | [DataMember]
96 | public string type = "roomJoin";
97 | [DataMember]
98 | public Room room;
99 |
100 | }
101 |
102 | ///
103 | /// Event is received as the response for EdgeManager.GetRooms()
104 | ///
105 | [DataContract]
106 | public class RoomsList
107 | {
108 | [DataMember]
109 | public string type = "roomsList";
110 | [DataMember]
111 | public List rooms;
112 | }
113 |
114 | ///
115 | /// Event is received as the response for EdgeManager.GetAvailableRooms()
116 | ///
117 | [DataContract]
118 | public class AvailableRoomsList
119 | {
120 | [DataMember]
121 | public string type = "availableRoomsList";
122 | [DataMember]
123 | public List availableRooms;
124 |
125 | }
126 |
127 | ///
128 | /// Event is received once a new member joins a room that the local player is a member of
129 | /// the room member has the new updated list of room members
130 | ///
131 | [DataContract]
132 | public class PlayerJoinedRoom
133 | {
134 | [DataMember]
135 | public string type = "playerJoinedRoom";
136 | [DataMember]
137 | public Room room;
138 | }
139 |
140 | ///
141 | /// Event is received once a room member leaves a room that the local player is a member of
142 | ///
143 | [DataContract]
144 | public class RoomMemberLeft
145 | {
146 | [DataMember]
147 | public string type = "memberLeft";
148 |
149 | [DataMember]
150 | public string idOfPlayerLeft;
151 |
152 | }
153 |
154 |
155 | ///
156 | /// Indicates the start of the Game
157 | /// Once the number of players in a room reaches the room maximum player limit the game starts
158 | /// (Ex. If a room have maximum players (3), once the server assigns the third player to the room, the server will send GameStartEvent to all the room members)
159 | ///
160 | [DataContract]
161 | public class GameStart
162 | {
163 | [DataMember]
164 | public string type = "gameStart";
165 |
166 | [DataMember]
167 | public Room room;
168 | }
169 |
170 | ///
171 | /// GamePlayEvent is the main event for GamePlayEvents
172 | ///
173 | [DataContract]
174 | public class GamePlayEvent
175 | {
176 | [DataMember(IsRequired = true)]
177 | public string type;
178 | [DataMember(IsRequired = true)]
179 | public string roomId;
180 | [DataMember(IsRequired = true)]
181 | public string senderId;
182 | [DataMember(IsRequired = true)]
183 | public string eventName;
184 | [DataMember(EmitDefaultValue = false)]
185 | public string[] stringData;
186 | [DataMember(EmitDefaultValue = false)]
187 | public int[] integerData;
188 | [DataMember(EmitDefaultValue = false)]
189 | public float[] floatData;
190 | [DataMember(EmitDefaultValue = false)]
191 | public bool[] booleanData;
192 |
193 | public GamePlayEvent()
194 | {
195 | type = "GamePlayEvent";
196 | }
197 |
198 | public GamePlayEvent(string eventName, Vector3 position)
199 | {
200 | type = "GamePlayEvent";
201 | this.eventName = eventName;
202 | floatData = Util.ConvertVector3ToFloatArray(position);
203 | }
204 |
205 | public GamePlayEvent(string eventName, Quaternion rotation)
206 | {
207 | type = "GamePlayEvent";
208 | this.eventName = eventName;
209 | floatData = Util.ConvertVector3ToFloatArray(rotation.eulerAngles);
210 | }
211 |
212 | public GamePlayEvent(string eventName, Vector3 position, Quaternion rotation)
213 | {
214 | type = "GamePlayEvent";
215 | this.eventName = eventName;
216 |
217 | floatData = new float[6]
218 | {
219 | position.x, position.y, position.z,rotation.eulerAngles.x, rotation.eulerAngles.y, rotation.eulerAngles.z
220 | };
221 | }
222 |
223 | public GamePlayEvent(string eventName, List scoreArray)
224 | {
225 | type = "GamePlayEvent";
226 | this.eventName = eventName;
227 | integerData = scoreArray.ToArray();
228 | }
229 |
230 | public GamePlayEvent(string eventName, List scoreArray)
231 | {
232 | type = "GamePlayEvent";
233 | this.eventName = eventName;
234 | floatData = scoreArray.ToArray();
235 | }
236 |
237 | public GamePlayEvent(string roomId, string playerId,
238 | string eventName, string[] stringData, int[] integerData, float[] floatData,
239 | bool[] booleanData)
240 | {
241 | type = "GamePlayEvent";
242 | this.roomId = roomId;
243 | senderId = playerId;
244 | this.eventName = eventName;
245 | this.stringData = stringData;
246 | this.integerData = integerData;
247 | this.floatData = floatData;
248 | this.booleanData = booleanData;
249 | }
250 |
251 | public string ToJson()
252 | {
253 | return JsonUtility.ToJson(this); ;
254 | }
255 | }
256 |
257 | [Serializable]
258 | public class Observable
259 | {
260 | ///
261 | /// The Transform you want to Sync its position and/or rotation
262 | ///
263 | public Transform observeredTransform;
264 | ///
265 | /// Synchronization Option
266 | ///
267 | public SyncOptions syncOption;
268 | ///
269 | /// Set to true if you want to smoothen the tracked position if you have network lag
270 | ///
271 | [Tooltip("Check if you want to smoothen the tracked position if you have network lag")]
272 | public bool InterpolatePosition;
273 | ///
274 | /// Set to true if you want to smoothen the tracked rotation if you have network lag
275 | ///
276 | [Tooltip("Check if you want to smoothen the tracked rotation if you have network lag")]
277 | public bool InterpolateRotation;
278 | ///
279 | /// Set Interpolation factor between 0.1 and 1
280 | ///
281 | [Tooltip("Set Interpolation factor between 0.1 and 1")]
282 | [Range(0.1f, 1f)]
283 | public float InterpolationFactor;
284 | [HideInInspector]
285 | public int observableIndex;
286 | [HideInInspector]
287 | public Vector3 lastPosition;
288 | [HideInInspector]
289 | public Vector3 lastRotation;
290 | [HideInInspector]
291 | public NetworkedPlayer owner;
292 | [HideInInspector]
293 | public bool attachedToPlayer;
294 |
295 | ///
296 | /// Set to true to allow ownership takeover by other players
297 | ///
298 | [Tooltip("Set to true to allow ownership takeover by other players")]
299 | public bool squattingAllowed;
300 |
301 | ///
302 | /// Observable Constructor
303 | ///
304 | /// The Transform you want to Sync its position and/or rotation
305 | /// Synchronization Option
306 | /// Set to true if you want to smoothen the tracked position if there is network lag
307 | /// Set to true if you want to smoothen the tracked rotation if there is network lag
308 | /// Set Interpolation factor value between 0.1 and 1
309 | /// Observable index in observer.observables list
310 | public Observable(Transform targetTransform, SyncOptions syncOption, bool interpolatePosition, bool interpolateRotation, float interpolationFactor, int observableIndex = 0)
311 | {
312 | this.observeredTransform = targetTransform;
313 | this.syncOption = syncOption;
314 | InterpolatePosition = interpolatePosition;
315 | InterpolateRotation = interpolateRotation;
316 | InterpolationFactor = interpolationFactor;
317 | this.observableIndex = observableIndex;
318 |
319 | }
320 | ///
321 | /// Observable Constructor
322 | ///
323 | /// The Transform you want to Sync its position and/or rotation
324 | /// Synchronization Option
325 | /// Set to true if you want to smoothen the tracked rotation if there is network lag
326 | /// Set to true if you want to smoothen the tracked rotation if there is network lag
327 | /// Set Interpolation factor value between 0.1 and 1
328 | public Observable(Transform targetTransform, SyncOptions syncOption, bool interpolatePosition, bool interpolateRotation, float interpolationFactor)
329 | {
330 | this.observeredTransform = targetTransform;
331 | this.syncOption = syncOption;
332 | InterpolatePosition = interpolatePosition;
333 | InterpolateRotation = interpolateRotation;
334 | InterpolationFactor = interpolationFactor;
335 | }
336 |
337 | public void SetObservableIndex(int index)
338 | {
339 | observableIndex = index;
340 | }
341 |
342 | public void SetupObservable(NetworkedPlayer observableOwner)
343 | {
344 | owner = observableOwner;
345 | switch (syncOption)
346 | {
347 | case SyncOptions.SyncPosition:
348 | lastPosition = observeredTransform.transform.position;
349 | break;
350 | case SyncOptions.SyncRotation:
351 | lastRotation = observeredTransform.transform.rotation.eulerAngles;
352 | break;
353 | case SyncOptions.SyncPositionAndRotation:
354 | lastPosition = observeredTransform.transform.position;
355 | lastRotation = observeredTransform.transform.rotation.eulerAngles;
356 | break;
357 | case SyncOptions.SyncLocalPosition:
358 | lastPosition = observeredTransform.transform.localPosition;
359 | break;
360 | case SyncOptions.SyncLocalRotation:
361 | lastRotation = observeredTransform.transform.localRotation.eulerAngles;
362 | break;
363 | case SyncOptions.SyncLocalPositionAndRotation:
364 | lastPosition = observeredTransform.transform.localPosition;
365 | lastRotation = observeredTransform.transform.localRotation.eulerAngles;
366 | break;
367 | }
368 | }
369 |
370 | public void SendDataToServer()
371 | {
372 | GamePlayEvent observerEvent = new GamePlayEvent();
373 | observerEvent.eventName = "EdgeMultiplayObserver";
374 | observerEvent.integerData = new int[2] { (int)syncOption, observableIndex };
375 | switch (syncOption)
376 | {
377 | case SyncOptions.SyncPosition:
378 | observerEvent.floatData = Util.GetPositionData(observeredTransform.transform);
379 | lastPosition = observeredTransform.transform.position;
380 | break;
381 | case SyncOptions.SyncRotation:
382 | observerEvent.floatData = Util.GetRotationEulerData(observeredTransform.transform);
383 | lastRotation = observeredTransform.transform.rotation.eulerAngles;
384 | break;
385 | case SyncOptions.SyncPositionAndRotation:
386 | observerEvent.floatData = Util.GetPositionAndRotationData(observeredTransform.transform);
387 | lastRotation = observeredTransform.transform.rotation.eulerAngles;
388 | lastPosition = observeredTransform.transform.position;
389 | break;
390 | case SyncOptions.SyncLocalPosition:
391 | observerEvent.floatData = Util.GetLocalPositionData(observeredTransform.transform);
392 | lastPosition = observeredTransform.localPosition;
393 | break;
394 | case SyncOptions.SyncLocalRotation:
395 | observerEvent.floatData = Util.GetLocalRotationData(observeredTransform.transform);
396 | lastRotation = observeredTransform.transform.rotation.eulerAngles;
397 | break;
398 | case SyncOptions.SyncLocalPositionAndRotation:
399 | observerEvent.floatData = Util.GetLocalPositionAndRotationData(observeredTransform.transform);
400 | lastPosition = observeredTransform.transform.localRotation.eulerAngles;
401 | lastRotation = observeredTransform.transform.localPosition;
402 | break;
403 | }
404 | EdgeManager.SendUDPMessage(observerEvent);
405 | }
406 |
407 | public void SetOwnership(string ownerId)
408 | {
409 | owner = EdgeManager.GetPlayer(ownerId);
410 | if (owner == null)
411 | {
412 | throw new Exception("EdgeMultiplay: Couldn't find player with id: " + ownerId);
413 | }
414 |
415 | if (owner.observer == null)
416 | {
417 | owner.gameObject.AddComponent();
418 | }
419 | owner.observer.observables.Add(this);
420 | owner.observer.UpdateObservables();
421 | }
422 | ///
423 | /// Changes the owner of an Observable object
424 | /// ChangeOwnership() will change the owner in the local player's world and
425 | /// update all room members about Ownership change.
426 | ///
427 | /// The new owner player id
428 | public void ChangeOwnership(string newOwnerId)
429 | {
430 | if (owner != null)
431 | {
432 | if (newOwnerId == owner.playerId)
433 | {
434 | Debug.LogWarning("EdgeMultiplay: No ownership change needed you already own the observable object");
435 | return;
436 | }
437 | if (owner.observer != null)
438 | {
439 | owner.observer.observables.Remove(this);
440 | }
441 | owner = EdgeManager.GetPlayer(newOwnerId);
442 | if (owner == null)
443 | {
444 | throw new Exception("EdgeMultiplay: Couldn't find player with id: " + newOwnerId);
445 | }
446 | if (owner.observer == null)
447 | {
448 | owner.gameObject.AddComponent();
449 | }
450 | owner.observer.observables.Add(this);
451 | owner.observer.UpdateObservables();
452 | GamePlayEvent changeOwnershipEvent = new GamePlayEvent()
453 | {
454 | eventName = "ObservableOwnershipChange",
455 | stringData = new string[1] { newOwnerId },
456 | integerData = new int[1] { observableIndex }
457 | };
458 | EdgeManager.MessageSender.BroadcastMessage(changeOwnershipEvent);
459 | }
460 | else
461 | {
462 | Debug.LogError("EdgeMultiplay: Observer has no owner, Use observer.SetOwnership() first");
463 | return;
464 | }
465 | }
466 | }
467 |
468 | #endregion
469 | #region Requests
470 |
471 | ///
472 | /// CreateRoomRequest is sent to the server to create a room
473 | ///
474 | [DataContract]
475 | public class CreateRoomRequest
476 | {
477 | [DataMember]
478 | public string type = "CreateRoom";
479 | [DataMember]
480 | public string playerId;
481 | [DataMember]
482 | public string playerName;
483 | [DataMember]
484 | public int playerAvatar;
485 | [DataMember]
486 | public int maxPlayersPerRoom;
487 | [DataMember]
488 | public int minPlayersToStartGame;
489 | [DataMember(EmitDefaultValue = false)]
490 | public Hashtable playerTags;
491 | public CreateRoomRequest(string PlayerName, int PlayerAvatar, int MaxPlayersPerRoom, Hashtable playerTags = null, int MinPlayersToStartGame = 0)
492 | {
493 | type = "CreateRoom";
494 | playerId = EdgeManager.gameSession.playerId;
495 | playerName = PlayerName;
496 | playerAvatar = PlayerAvatar;
497 | maxPlayersPerRoom = MaxPlayersPerRoom;
498 | minPlayersToStartGame = MinPlayersToStartGame;
499 | this.playerTags = playerTags;
500 | }
501 | }
502 |
503 | ///
504 | /// JoinOrCreateRoomRequest is sent to the server to join a room or create a new room if there are no available rooms
505 | ///
506 | [DataContract]
507 | public class JoinOrCreateRoomRequest
508 | {
509 | [DataMember]
510 | public string type = "JoinOrCreateRoom";
511 | [DataMember]
512 | public string playerId;
513 | [DataMember]
514 | public string playerName;
515 | [DataMember]
516 | public int playerAvatar;
517 | [DataMember]
518 | public int maxPlayersPerRoom;
519 | [DataMember]
520 | public int minPlayersToStartGame;
521 | [DataMember(EmitDefaultValue = false)]
522 | public Hashtable playerTags;
523 |
524 | public JoinOrCreateRoomRequest(string PlayerName, int PlayerAvatar, int MaxPlayersPerRoom, Hashtable playerTags = null, int MinPlayersToStartGame = 0)
525 | {
526 | type = "JoinOrCreateRoom";
527 | playerId = EdgeManager.gameSession.playerId;
528 | playerName = PlayerName;
529 | playerAvatar = PlayerAvatar;
530 | maxPlayersPerRoom = MaxPlayersPerRoom;
531 | minPlayersToStartGame = MinPlayersToStartGame;
532 | this.playerTags = playerTags;
533 | }
534 | }
535 |
536 | ///
537 | /// JoinRoomRequest is sent to the server to join a specific room
538 | ///
539 | [DataContract]
540 | public class JoinRoomRequest
541 | {
542 | [DataMember]
543 | public string type = "JoinRoom";
544 | [DataMember]
545 | public string playerId;
546 | [DataMember]
547 | public string playerName;
548 | [DataMember]
549 | public int playerAvatar;
550 | [DataMember]
551 | public string roomId;
552 | [DataMember(EmitDefaultValue = false)]
553 | public Hashtable playerTags;
554 |
555 | public JoinRoomRequest(string RoomId, string PlayerName, int PlayerAvatar, Hashtable playerTags = null)
556 | {
557 | type = "JoinRoom";
558 | roomId = RoomId;
559 | playerId = EdgeManager.gameSession.playerId;
560 | playerName = PlayerName;
561 | playerAvatar = PlayerAvatar;
562 | this.playerTags = playerTags;
563 | }
564 | }
565 |
566 | ///
567 | /// GetRoomsRequest is sent to the server to get a list of the rooms on the server
568 | ///
569 | [DataContract]
570 | public class GetRoomsRequest
571 | {
572 | [DataMember]
573 | public string type = "GetRooms";
574 | public GetRoomsRequest()
575 | {
576 | type = "GetRooms";
577 | }
578 | }
579 |
580 | ///
581 | /// GetAvailableRooms is sent to the server to get a list of the availble rooms on the server
582 | /// available room is a room that is not full
583 | /// room x is available if room x have 2 members and the maximum players per room is 3
584 | ///
585 | [DataContract]
586 | public class GetAvailableRoomsRequest
587 | {
588 | [DataMember]
589 | public string type = "GetAvailableRooms";
590 | public GetAvailableRoomsRequest()
591 | {
592 | type = "GetAvailableRooms";
593 | }
594 | }
595 |
596 | [DataContract]
597 | public class ExitRoomRequest
598 | {
599 | [DataMember]
600 | public string type = "ExitRoom";
601 | [DataMember]
602 | public string playerId;
603 | [DataMember]
604 | public string roomId;
605 |
606 | public ExitRoomRequest()
607 | {
608 | type = "ExitRoom";
609 | playerId = EdgeManager.gameSession.playerId;
610 | roomId = EdgeManager.gameSession.roomId;
611 | }
612 | }
613 |
614 | #endregion
615 | #region EdgeMultiplay Helper Classes
616 |
617 | ///
618 | /// Wrapper class to hold game session data received from the server
619 | ///
620 | public class Session
621 | {
622 | public string sessionId;
623 | public string roomId = "";
624 | public string playerId;
625 | public string playerName;
626 | public string playerAvatar;
627 | public int playerIndex;
628 | public Player[] currentPlayers;
629 | }
630 |
631 | ///
632 | /// Wrapper class for room info received the server
633 | ///
634 | [DataContract]
635 | public class Room
636 | {
637 | ///
638 | /// Room Id assigned on ther server
639 | ///
640 | [DataMember]
641 | public string roomId;
642 |
643 | ///
644 | /// Generic List of players in the room
645 | ///
646 | [DataMember]
647 | public List roomMembers;
648 |
649 | ///
650 | /// Maximum Number of players per room
651 | ///
652 | [DataMember]
653 | public int maxPlayersPerRoom;
654 |
655 | ///
656 | /// The minimum players threshold to start a game, if less than 2, minPlayersPerRoom == maxPlayersPerRoom
657 | ///
658 | [DataMember]
659 | public int minPlayersToStartGame;
660 |
661 | ///
662 | /// Indicates that the game is running in the room
663 | ///
664 | [DataMember]
665 | public bool gameStarted;
666 | }
667 |
668 | ///
669 | /// Wrapper class for the player info received from the server
670 | ///
671 | [DataContract]
672 | public class Player
673 | {
674 | ///
675 | /// Unique ID assigned to player once the connection is established with the server, assigned on RegisterEvent
676 | ///
677 | [HideInInspector]
678 | [DataMember]
679 | public string playerId;
680 |
681 | [DataMember]
682 | public string playerName;
683 |
684 | ///
685 | /// the player order in the room, ex. the first player to join a room has playerIndex of 0
686 | ///
687 | [DataMember]
688 | public int playerIndex;
689 | ///
690 | /// the player Avatar will be selected from EdgeManager SpawnPrefabs Array by index
691 | /// (ex. if playerAvatar=3 then the player will take the Avatar from EdgeManager.SpawnPrefabs[3]
692 | ///
693 | [DataMember]
694 | public int playerAvatar = 0;
695 |
696 | ///
697 | /// helper instance variable for serializing and deserializing playerTags
698 | ///
699 | [DataMember(EmitDefaultValue = false)]
700 | #pragma warning disable 0649
701 | internal Hashtable playerTags;
702 |
703 | ///
704 | /// Dictionary custom data associated with the player
705 | ///
706 | public Dictionary playerTagsDict
707 | {
708 | get
709 | {
710 | if (playerTags != null)
711 | {
712 | return Tag.HashtableToDictionary(playerTags);
713 | }
714 | else
715 | {
716 | return null;
717 | }
718 | }
719 | }
720 | }
721 |
722 | ///
723 | /// Wrapper class to hold the return of GamePlayEvent.GetPositionAndRotation()
724 | ///
725 | public class PositionAndRotation
726 | {
727 | public Vector3 position;
728 | public Quaternion rotation;
729 |
730 | public PositionAndRotation(Vector3 vector3, Quaternion quaternion)
731 | {
732 | position = vector3;
733 | rotation = quaternion;
734 | }
735 | }
736 |
737 | ///
738 | /// Wrapper class for Position And Rotation Eulers Used for EdgeManager Spawn Info
739 | ///
740 | [Serializable]
741 | public class PositionAndRotationEulers
742 | {
743 | public Vector3 position;
744 | ///
745 | /// Rotation Eulers similar as to the Rotation in the inspector
746 | ///
747 | public Vector3 rotation;
748 | }
749 |
750 | #endregion
751 | #region Serialization Helpers
752 |
753 | [DataContract]
754 | class MessageWrapper
755 | {
756 | [DataMember]
757 | public string type = "utf8";
758 | [DataMember]
759 | public string utf8Data;
760 | public static MessageWrapper WrapTextMessage(string jsonStr)
761 | {
762 | var wrapper = new MessageWrapper();
763 | wrapper.utf8Data = jsonStr;
764 | return wrapper;
765 | }
766 | public static MessageWrapper UnWrapMessage(string wrappedJsonStr)
767 | {
768 | var wrapper = Messaging.Deserialize(wrappedJsonStr);
769 | return wrapper;
770 | }
771 | }
772 |
773 | static class Messaging
774 | {
775 | private static string StreamToString(Stream s)
776 | {
777 | s.Position = 0;
778 | StreamReader reader = new StreamReader(s);
779 | string jsonStr = reader.ReadToEnd();
780 | return jsonStr;
781 | }
782 |
783 | public static string Serialize(T t)
784 | {
785 | DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
786 | MemoryStream ms = new MemoryStream();
787 |
788 | serializer.WriteObject(ms, t);
789 | string jsonStr = StreamToString(ms);
790 |
791 | return jsonStr;
792 | }
793 |
794 | public static T Deserialize(string jsonString)
795 | {
796 | using (MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString ?? "")))
797 | {
798 | return Deserialize(memStream);
799 | }
800 | }
801 |
802 | public static T Deserialize(Stream stream)
803 | {
804 | DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T));
805 | T t = (T)deserializer.ReadObject(stream);
806 | return t;
807 | }
808 | }
809 |
810 | #endregion
811 | }
812 |
--------------------------------------------------------------------------------
/EdgeMultiplay/Scripts/EdgeManager.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018-2021 MobiledgeX, Inc. All rights and licenses reserved.
3 | * MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | using System.Collections;
19 | using System.Collections.Generic;
20 | using UnityEngine;
21 | using System;
22 | using System.Text;
23 | using MobiledgeX;
24 | using DistributedMatchEngine;
25 | using System.Threading.Tasks;
26 |
27 | namespace EdgeMultiplay
28 | {
29 | ///
30 | /// EdgeManager is the class responsible for Connection to the server, Handling Server Events and storing game session
31 | /// EdgeManager requires LocationService if you are using a MobiledgeX Server
32 | /// EdgeManager have many static variables, your app/game should have only one EdgeManager
33 | ///
34 | [RequireComponent(typeof(MobiledgeX.LocationService))]
35 | [AddComponentMenu("EdgeMultiplay/EdgeManager")]
36 | public class EdgeManager : MonoBehaviour
37 | {
38 | #region EdgeManager static variables and constants
39 |
40 | public static MobiledgeXIntegration integration;
41 | static MobiledgeXWebSocketClient wsClient;
42 | static MobiledgeXUDPClient udpClient;
43 | ///
44 | /// The Game Session stored after the player is registered on the server
45 | ///
46 | public static Session gameSession;
47 | ///
48 | /// list of the current players in the same room as the local player
49 | ///
50 | public static List currentRoomPlayers = new List();
51 | ///
52 | /// Represents the local player, to send messages to the server use EdgeManager.MessageSender.BroadcastMessage()
53 | ///
54 | public static NetworkedPlayer MessageSender;
55 | public static NetworkedPlayer localPlayer;
56 | ///
57 | /// Indicates that the game have already started
58 | ///
59 | public static bool gameStarted;
60 | public static List observers = new List();
61 |
62 | ///
63 | /// If you want to have a different World Origin, Players will be spawned relative to the Transform specified
64 | ///
65 | public Transform WorldOriginTransform;
66 | #endregion
67 |
68 | #region private variables
69 |
70 | byte[] udpMsg;
71 | string wsMsg;
72 |
73 | #endregion
74 |
75 | #region EdgeManager Editor Exposed Variables
76 |
77 | ///
78 | /// List of GameObjects to be used as player avatar
79 | ///
80 | [Header("Players", order = 0)]
81 | [Tooltip("Player Avatars to be instatiated in once the Game starts")]
82 | public List SpawnPrefabs;
83 |
84 | ///
85 | /// List of Spawn Info, Spawn Info consists of Position and the Rotation Eulers
86 | ///
87 | [Tooltip("Positions for spawning player prefabs, the order is based on who connects first")]
88 | public List SpawnInfo;
89 | private static EdgeManager instance;
90 | #endregion
91 |
92 | #region MonoBehaviour Callbacks
93 | private void Awake()
94 | {
95 | DontDestroyOnLoad(this);
96 | if (instance == null)
97 | {
98 | instance = this;
99 | }
100 | else
101 | {
102 | Destroy(gameObject);
103 | }
104 | integration = new MobiledgeXIntegration();
105 | }
106 |
107 | void Update()
108 | {
109 | if (wsClient == null)
110 | {
111 | return;
112 | }
113 | //websocket receive queue
114 | var ws_queue = wsClient.receiveQueue;
115 | while (ws_queue.TryPeek(out wsMsg))
116 | {
117 | ws_queue.TryDequeue(out wsMsg);
118 | HandleWebSocketMessage(wsMsg);
119 | wsMsg = null;
120 | }
121 | if (udpClient == null)
122 | {
123 | return;
124 | }
125 | //udp receive queue
126 | while (udpClient.receiveQueue.TryPeek(out udpMsg))
127 | {
128 | udpClient.receiveQueue.TryDequeue(out udpMsg);
129 | HandleUDPMessage(Encoding.UTF8.GetString(udpMsg));
130 | udpMsg = null;
131 | }
132 | }
133 |
134 | void OnDestroy()
135 | {
136 | observers = null;
137 | currentRoomPlayers = null;
138 | if (wsClient != null)
139 | {
140 | wsClient.tokenSource.Cancel();
141 | }
142 | if (udpClient != null)
143 | {
144 | udpClient.Dispose();
145 | }
146 | if (Application.platform == RuntimePlatform.OSXPlayer
147 | || Application.platform == RuntimePlatform.WindowsPlayer
148 | || Application.platform == RuntimePlatform.LinuxPlayer)
149 | {
150 | Environment.Exit(0);
151 | }
152 | }
153 |
154 | #endregion
155 |
156 | #region Static EdgeManager functions
157 | ///
158 | /// Connect to your EdgeMultiplay server based on location and carrier info
159 | ///
160 | /// use public override void OnConnectionToEdge() to get the server response
161 | ///
162 | ///
163 | /// set to true for connection based on location info only
164 | /// set to true to use overloaded location sat in setFallbackLocation()
165 | /// You can specify a path for your connection to be verified on the server side
166 | /// Connection Task, use OnConnectionToEdge() to listen to the async task result
167 | public async Task ConnectToServer(bool useAnyCarrierNetwork = true, bool useFallBackLocation = false, string path = "")
168 | {
169 | if (Configs.clientSettings.useLocalHostServer)
170 | {
171 | try
172 | {
173 | gameSession = new Session();
174 | wsClient = new MobiledgeXWebSocketClient();
175 | Uri uri = new Uri("ws://" + Configs.clientSettings.hostIPAddress + ":" + Configs.clientSettings.WebSocketPort + path);
176 | if (wsClient.isOpen())
177 | {
178 | wsClient.Dispose();
179 | wsClient = new MobiledgeXWebSocketClient();
180 | }
181 | await wsClient.Connect(@uri);
182 | EdgeMultiplayCallbacks.connectedToEdge();
183 | }
184 | catch (Exception e)
185 | {
186 | EdgeMultiplayCallbacks.failureToConnect(e.Message);
187 | Debug.LogError("EdgeMultiplay: Failed to connect to your Local Host, Check console for more details");
188 | }
189 | }
190 | else
191 | {
192 | try
193 | {
194 | gameSession = new Session();
195 | integration.UseWifiOnly(useAnyCarrierNetwork);
196 | integration.useFallbackLocation = useFallBackLocation;
197 | wsClient = new MobiledgeXWebSocketClient();
198 | await integration.RegisterAndFindCloudlet();
199 | integration.GetAppPort(LProto.L_PROTO_TCP, Configs.clientSettings.WebSocketPort);
200 | string url = integration.GetUrl("ws") + path;
201 | Uri uri = new Uri(url);
202 | if (wsClient.isOpen())
203 | {
204 | wsClient.Dispose();
205 | wsClient = new MobiledgeXWebSocketClient();
206 | }
207 | await wsClient.Connect(@uri);
208 | EdgeMultiplayCallbacks.connectedToEdge();
209 | }
210 | catch (Exception e)
211 | {
212 | EdgeMultiplayCallbacks.failureToConnect(e.Message);
213 | Debug.LogError("EdgeMultiplay: Failed to connect to Edge, Check console for more details");
214 | }
215 | }
216 | }
217 |
218 | ///
219 | /// Get Player using the player id
220 | ///
221 | /// playerId is a unique Id assigned to player during OnRegister() and saved into EdgeManager.gameSession.playerId
222 | /// The NetworkedPlayer of the supplied playerId
223 | public static NetworkedPlayer GetPlayer(string playerId)
224 | {
225 | NetworkedPlayer playerRequested;
226 | playerRequested = currentRoomPlayers.Find(player => player.playerId == playerId);
227 | if (playerRequested == null)
228 | {
229 | Debug.LogError("Error finding player with id: " + playerId);
230 | }
231 | return playerRequested;
232 | }
233 |
234 | ///
235 | /// Get Player using the player index in the room
236 | ///
237 | /// playerIndex is an id assigned to player based on the precedence of joining the room
238 | /// The NetworkedPlayer of the supplied playerIndex
239 | public static NetworkedPlayer GetPlayer(int playerIndex)
240 | {
241 | NetworkedPlayer playerRequested;
242 | playerRequested = currentRoomPlayers.Find(player => player.playerIndex == playerIndex);
243 | if (playerRequested == null)
244 | {
245 | Debug.LogError("Error finding player with index: " + playerIndex);
246 | }
247 | return playerRequested;
248 | }
249 |
250 | ///
251 | /// Sends Join Or Create Room request to the server, the server will try to match a player with any available room
252 | /// if the server didn't find an available room, the server will create a room for the player
253 | ///
254 | /// use public override void OnRoomCreated or public override void OnRoomJoin to get the server response
255 | ///
256 | ///
257 | /// player name to be assigned to player
258 | /// (integer value) Avatar Index from EdgeManager Spawn Prefabs
259 | /// In case of room creation, the maximum players allowed in the room
260 | /// In case of room creation, the minimum players threshold to start a game, if less than 2, minPlayersPerRoom == maxPlayersPerRoom
261 | /// Dictionary custom data associated with the player
262 | public static void JoinOrCreateRoom(string playerName, int playerAvatar, int maxPlayersPerRoom, Dictionary playerTags = null, int minPlayersToStartGame = 0)
263 | {
264 | if (maxPlayersPerRoom < 2)
265 | {
266 | Debug.LogError("EdgeMultiplay : maxPlayersPerRoom must be greater than 1");
267 | return;
268 | }
269 | Hashtable playertagsHashtable;
270 | if (playerTags != null)
271 | {
272 | playertagsHashtable = Tag.DictionaryToHashtable(playerTags);
273 | }
274 | else
275 | {
276 | playertagsHashtable = null;
277 | }
278 | JoinOrCreateRoomRequest createOrJoinRoomRequest = new JoinOrCreateRoomRequest(playerName, playerAvatar, maxPlayersPerRoom, playertagsHashtable, minPlayersToStartGame);
279 | wsClient.Send(Messaging.Serialize(createOrJoinRoomRequest));
280 | }
281 |
282 | ///
283 | /// Sends a request to the server to get a full list of rooms on the servers
284 | ///
285 | /// use public override void OnRoomsListReceived to get the server response
286 | ///
287 | ///
288 | public static void GetRooms()
289 | {
290 | GetRoomsRequest getRoomsRequest = new GetRoomsRequest();
291 | wsClient.Send(Messaging.Serialize(getRoomsRequest));
292 | }
293 |
294 | ///
295 | /// Sends a request to the server to get a full list of rooms on the servers
296 | ///
297 | /// use public override void OnRoomsListReceived to get the server response
298 | ///
299 | ///
300 | public static void GetAvailableRooms()
301 | {
302 | GetAvailableRoomsRequest getAvailableRoomsRequest = new GetAvailableRoomsRequest();
303 | wsClient.Send(Messaging.Serialize(getAvailableRoomsRequest));
304 | }
305 |
306 | ///
307 | /// Sends a request to the server to create a room
308 | ///
309 | /// use public override void OnRoomCreated to get the server response
310 | ///
311 | ///
312 | /// player name to be assigned to player
313 | /// (integer value) Avatar Index from EdgeManager Spawn Prefabs
314 | /// The maximum players allowed in the room
315 | /// In case of room creation, the minimum players threshold to start a game, if less than 2, minPlayersPerRoom == maxPlayersPerRoom
316 | /// Dictionary custom data associated with the player
317 | public static void CreateRoom(string playerName, int playerAvatar, int maxPlayersPerRoom, Dictionary playerTags = null, int minPlayersToStartGame = 0)
318 | {
319 | if (maxPlayersPerRoom < 2)
320 | {
321 | Debug.LogError("EdgeMultiplay : maxPlayersPerRoom must be greater than 1");
322 | return;
323 | }
324 | // Assure Player is not already a member of another room
325 | if (gameSession.roomId == "")
326 | {
327 | Hashtable playertagsHashtable;
328 | if (playerTags != null)
329 | {
330 | playertagsHashtable = Tag.DictionaryToHashtable(playerTags);
331 | }
332 | else
333 | {
334 | playertagsHashtable = null;
335 | }
336 | CreateRoomRequest createRoomRequest = new CreateRoomRequest(playerName, playerAvatar, maxPlayersPerRoom, playertagsHashtable, minPlayersToStartGame);
337 | wsClient.Send(Messaging.Serialize(createRoomRequest));
338 | }
339 | else
340 | {
341 | Debug.LogError("EdgeMultiplay : Player is already a member in another room");
342 | }
343 | }
344 |
345 | ///
346 | /// Sends a request to the server to join a room
347 | ///
348 | /// use public override void OnRoomJoin to get the server response
349 | ///
350 | ///
351 | /// Id of the room intended to join
352 | /// (integer value) Avatar Index from EdgeManager Spawn Prefabs
353 | /// Dictionary custom data associated with the player
354 | public static void JoinRoom(string roomId, string playerName, int playerAvatar, Dictionary playerTags = null)
355 | {
356 | if (gameSession.roomId == "")
357 | {
358 | Hashtable playertagsHashtable;
359 | if (playerTags != null)
360 | {
361 | playertagsHashtable = Tag.DictionaryToHashtable(playerTags);
362 | }
363 | else
364 | {
365 | playertagsHashtable = null;
366 | }
367 | JoinRoomRequest joinRoomRequest = new JoinRoomRequest(roomId, playerName, playerAvatar, playertagsHashtable);
368 | wsClient.Send(Messaging.Serialize(joinRoomRequest));
369 | }
370 | else
371 | {
372 | if (gameSession.roomId == roomId)
373 | {
374 | Debug.LogError("EdgeMultiplay : Player is already a member in this room");
375 | }
376 | else
377 | {
378 | Debug.LogError("EdgeMultiplay : Player is already a member in another room");
379 | }
380 | }
381 | }
382 |
383 | ///
384 | /// Sends a UDP Message from local player to other room members, can be used only after the game starts (OnGameStart())
385 | ///
386 | /// on the player manager that inherits from NetworkedPlayer use
387 | /// public override void OnMessageReceived to get the forwarded message
388 | ///
389 | ///
390 | /// UDP Messages are limited to 508 bytes if you want to exceed that make sure you slice and reassemble your buffer
391 | ///
392 | ///
393 | /// the GamePlay Event to be forwarded to other room members
394 | public static void SendUDPMessage(GamePlayEvent gameplayEvent)
395 | {
396 | if (!gameStarted)
397 | {
398 | Debug.LogError("Cannot perform this operation, Game didn't start yet");
399 | return;
400 | }
401 | gameplayEvent.roomId = gameSession.roomId;
402 | gameplayEvent.senderId = gameSession.playerId;
403 | if (udpClient.run)
404 | {
405 | udpClient.Send(gameplayEvent.ToJson());
406 | }
407 | else
408 | {
409 | Debug.LogError("EdgeMultiplay: Error in sending UDP Message");
410 | }
411 | }
412 |
413 | ///
414 | /// Sends a WebSocket Message from local player to other room members, can be used only after the game starts (OnGameStart())
415 | ///
416 | /// on the player manager that inherits from NetworkedPlayer use
417 | /// public override void OnMessageReceived to get the forwarded message
418 | ///
419 | ///
420 | ///
421 | public void SendGamePlayEvent(GamePlayEvent gamePlayEvent)
422 | {
423 | if (!gameStarted)
424 | {
425 | Debug.LogError("Cannot perform this operation, Game didn't start yet");
426 | return;
427 | }
428 | gamePlayEvent.roomId = gameSession.roomId;
429 | gamePlayEvent.senderId = gameSession.playerId;
430 | wsClient.Send(Messaging.Serialize(gamePlayEvent));
431 | }
432 |
433 | ///
434 | /// Kill the connection to your EdgeMultiplay server
435 | ///
436 | public static void Disconnect()
437 | {
438 | wsClient = null;
439 | udpClient = null;
440 | foreach (NetworkedPlayer player in currentRoomPlayers)
441 | {
442 | Destroy(player.gameObject);
443 | }
444 | currentRoomPlayers = new List();
445 | gameSession = new Session();
446 | gameStarted = false;
447 | MessageSender = null;
448 | localPlayer = null;
449 | observers = new List();
450 | }
451 |
452 | ///
453 | /// Exit the current room you are in
454 | ///
455 | public static void ExitRoom()
456 | {
457 | ExitRoomRequest exitRoomRequest = new ExitRoomRequest();
458 | wsClient.Send(Messaging.Serialize(exitRoomRequest));
459 | }
460 |
461 | #endregion
462 |
463 | #region EdgeManager Functions
464 |
465 | void HandleWebSocketMessage(string message)
466 | {
467 | var msg = MessageWrapper.UnWrapMessage(message);
468 | switch (msg.type)
469 | {
470 | case "register":
471 | Register register = Messaging.Deserialize(message);
472 | gameSession.sessionId = register.sessionId;
473 | gameSession.playerId = register.playerId;
474 | EdgeMultiplayCallbacks.registerEvent();
475 | break;
476 |
477 | case "notification":
478 | Notification notification = Messaging.Deserialize(message);
479 | switch (notification.notificationText)
480 | {
481 | case "left-room":
482 | gameSession.roomId = "";
483 | EdgeMultiplayCallbacks.leftRoom();
484 | break;
485 | case "join-room-faliure":
486 | EdgeMultiplayCallbacks.joinRoomFaliure();
487 | break;
488 | case "new-room-created-in-lobby":
489 | EdgeMultiplayCallbacks.newRoomCreatedInLobby();
490 | break;
491 | case "room-removed-from-lobby":
492 | EdgeMultiplayCallbacks.roomRemovedFromLobby();
493 | break;
494 | case "rooms-updated":
495 | EdgeMultiplayCallbacks.roomsUpdated();
496 | break;
497 | }
498 | EdgeMultiplayCallbacks.notificationEvent(notification);
499 | break;
500 |
501 | case "roomsList":
502 | RoomsList roomsList = Messaging.Deserialize(message);
503 | EdgeMultiplayCallbacks.roomsList(roomsList.rooms);
504 | break;
505 |
506 | case "roomCreated":
507 | RoomCreated roomCreated = Messaging.Deserialize(message);
508 | gameSession.roomId = roomCreated.room.roomId;
509 | EdgeMultiplayCallbacks.roomCreated(roomCreated.room);
510 | break;
511 |
512 | case "roomJoin":
513 | RoomJoin roomJoin = Messaging.Deserialize(message);
514 | gameSession.roomId = roomJoin.room.roomId;
515 | EdgeMultiplayCallbacks.roomJoin(roomJoin.room);
516 | int roomMembersCount = roomJoin.room.roomMembers.Count;
517 | if (roomMembersCount >= roomJoin.room.minPlayersToStartGame) //game already started
518 | {
519 | StartGame(roomJoin.room);
520 | EdgeMultiplayCallbacks.gameStart();
521 | }
522 | break;
523 |
524 | case "playerJoinedRoom":
525 | PlayerJoinedRoom playerJoinedRoom = Messaging.Deserialize(message);
526 | EdgeMultiplayCallbacks.playerRoomJoined(playerJoinedRoom.room);
527 | if (playerJoinedRoom.room.roomMembers[playerJoinedRoom.room.roomMembers.Count - 1].playerId == gameSession.playerId)
528 | {
529 | break;
530 | }
531 | else
532 | {
533 | if (gameStarted)
534 | {
535 | NetworkedPlayer networkedPlayer = SpawnPlayer(playerJoinedRoom.room.roomMembers[playerJoinedRoom.room.roomMembers.Count - 1]);
536 | currentRoomPlayers.Add(networkedPlayer);
537 | EdgeMultiplayCallbacks.eventReceived += networkedPlayer.OnWebSocketEventReceived;
538 | EdgeMultiplayCallbacks.udpEventReceived += networkedPlayer.OnUDPEventReceived;
539 | }
540 | else if (playerJoinedRoom.room.roomMembers.Count == playerJoinedRoom.room.minPlayersToStartGame)
541 | {
542 | StartGame(playerJoinedRoom.room);
543 | EdgeMultiplayCallbacks.gameStart();
544 | }
545 | }
546 | break;
547 |
548 | case "GamePlayEvent":
549 | GamePlayEvent gamePlayEvent = Messaging.Deserialize(message);
550 | switch (gamePlayEvent.eventName)
551 | {
552 | case "NewObservableCreated":
553 | CreateObservableObject(gamePlayEvent);
554 | break;
555 | case "ObservableOwnershipChange":
556 | UpdateObserverOwnership(gamePlayEvent);
557 | break;
558 | case "TakeOverObservable":
559 | TakeOverObservable(gamePlayEvent);
560 | break;
561 | case "OwnershipRequest":
562 | OwnershipRequestReceived(gamePlayEvent);
563 | break;
564 | default:
565 | ReflectEvent(gamePlayEvent);
566 | break;
567 | }
568 | break;
569 |
570 | case "memberLeft":
571 | RoomMemberLeft playerLeft = Messaging.Deserialize(message);
572 | EdgeMultiplayCallbacks.playerLeft(playerLeft);
573 | break;
574 |
575 | default:
576 | Debug.LogError("Unknown WebSocket message arrived: " + msg.type + ", message: " + message);
577 | break;
578 | }
579 | }
580 |
581 | void StartGame(Room room)
582 | {
583 | gameSession.currentPlayers = room.roomMembers.ToArray();
584 | foreach (Player player in room.roomMembers)
585 | {
586 | if (player.playerId == gameSession.playerId)
587 | {
588 | gameSession.playerIndex = player.playerIndex;
589 | }
590 | }
591 | CreatePlayers(gameSession.currentPlayers);
592 | gameStarted = true;
593 | if (Configs.clientSettings.useLocalHostServer)
594 | {
595 | udpClient = new MobiledgeXUDPClient(Configs.clientSettings.hostIPAddress, Configs.clientSettings.UDPPort);
596 | }
597 | else
598 | {
599 | udpClient = new MobiledgeXUDPClient(integration.GetHost(), integration.GetAppPort(LProto.L_PROTO_UDP, Configs.clientSettings.UDPPort).public_port);
600 | }
601 | SendUDPMessage(new GamePlayEvent() { eventName = "Start" });
602 | }
603 |
604 | void HandleUDPMessage(string message)
605 | {
606 | GamePlayEvent gamePlayEvent = JsonUtility.FromJson(message);
607 | switch (gamePlayEvent.type)
608 | {
609 | case "GamePlayEvent":
610 | if (gamePlayEvent.eventName == "EdgeMultiplayObserver")
611 | {
612 | SyncObject(gamePlayEvent);
613 | }
614 | else
615 | {
616 | // if used for other than that Syncing GameObjects Position & Rotation
617 | // it wil trigger OnUDPMessagesReceived()
618 | EdgeMultiplayCallbacks.udpEventReceived(gamePlayEvent);
619 | }
620 | break;
621 | default:
622 | Debug.LogError("Unknown UDP message arrived: " + message);
623 | break;
624 | }
625 | }
626 |
627 | void CreateObservableObject(GamePlayEvent newObservableEvent)
628 | {
629 | if (!gameStarted)
630 | {
631 | Debug.LogError("Cannot create observables, Game didn't start yet");
632 | return;
633 | }
634 | if (localPlayer.playerId == newObservableEvent.stringData[0])
635 | {
636 | return;
637 | }
638 | NetworkedPlayer playerCreatedObserver = GetPlayer(newObservableEvent.stringData[0]);
639 | Observable observable = playerCreatedObserver.CreateObservableObject(
640 | prefabName: newObservableEvent.stringData[1],
641 | startPosition: Util.ConvertFloatArrayToVector3(newObservableEvent.floatData, 0),
642 | startRotation: Quaternion.Euler(Util.ConvertFloatArrayToVector3(newObservableEvent.floatData, 3)),
643 | syncOption: (SyncOptions)Enum.ToObject(typeof(SyncOptions), newObservableEvent.integerData[0]),
644 | interpolatePosition: newObservableEvent.booleanData[0],
645 | interpolateRotation: newObservableEvent.booleanData[1],
646 | interpolationFactor: newObservableEvent.floatData[6]);
647 | EdgeMultiplayCallbacks.newObservableCreated(observable);
648 |
649 | }
650 |
651 | ///
652 | /// When an observable object change its owner, OwnershipChangeEvent is sent from the owner to other players
653 | /// Changing ownership must occur at the owner world
654 | ///
655 | /// OwnershipChangeEvent contains (oldOwnerId, the observable Index and the newOwnerId)
656 | void UpdateObserverOwnership(GamePlayEvent ownershipChangeEvent)
657 | {
658 | if (localPlayer.playerId == ownershipChangeEvent.senderId)
659 | {
660 | return;
661 | }
662 | NetworkedPlayer oldOwner = GetPlayer(ownershipChangeEvent.senderId);
663 | Observable observer;
664 | if (oldOwner.observer != null)
665 | {
666 | observer = oldOwner.observer.observables[ownershipChangeEvent.integerData[0]];
667 | }
668 | else
669 | {
670 | Debug.LogError("Couldn't find old owner.observer");
671 | return;
672 | }
673 | NetworkedPlayer newOwner = GetPlayer(ownershipChangeEvent.stringData[0]);
674 | if (newOwner.observer == null)
675 | {
676 | newOwner.gameObject.AddComponent();
677 | }
678 | newOwner.observer.observables.Add(observer);
679 | newOwner.observer.UpdateObservables();
680 | oldOwner.observer.observables.RemoveAt(ownershipChangeEvent.integerData[0]);
681 | }
682 |
683 | void TakeOverObservable(GamePlayEvent gamePlayEvent)
684 | {
685 | // check if the observable owner is the local player
686 | if (gamePlayEvent.stringData[0] == localPlayer.playerId)
687 | {
688 | //get the observable
689 | Observable observable = localPlayer.observer.observables
690 | .Find(obs => obs.observableIndex == gamePlayEvent.integerData[0]);
691 | if (observable == null)
692 | {
693 | Debug.LogWarning("EdgeMultiplay: couldn't find the observable");
694 | return;
695 | }
696 | // change the observable ownership from the current owner to the sender
697 | observable.ChangeOwnership(gamePlayEvent.senderId);
698 | }
699 | }
700 |
701 | void OwnershipRequestReceived(GamePlayEvent gamePlayEvent)
702 | {
703 | // check if the observable owner is the local player
704 | if (gamePlayEvent.stringData[0] == localPlayer.playerId)
705 | {
706 | //get the observable
707 | Observable observable = localPlayer.observer.observables
708 | .Find(obs => obs.observableIndex == gamePlayEvent.integerData[0]);
709 | if (observable == null)
710 | {
711 | Debug.LogWarning("EdgeMultiplay: couldn't find the observable");
712 | return;
713 | }
714 | NetworkedPlayer requestee = GetPlayer(gamePlayEvent.senderId);
715 |
716 | // Inform the current owner about the ownership request
717 | // Triggers OnOwnershipRequestReceived() callback
718 | localPlayer.ownershipRequested(requestee, observable);
719 | }
720 | }
721 |
722 | ///
723 | /// If the LocalPlayer is observing any transforms, once there is any update to the observed transform
724 | /// the local player will send the updated transfrom to its clones in the other players' world.
725 | ///
726 | /// the received gameplay event contains (observable owner id, observable index, syncOption, updated transform data)
727 | void SyncObject(GamePlayEvent receivedEvent)
728 | {
729 | if (receivedEvent.senderId == localPlayer.playerId)
730 | {
731 | return;
732 | }
733 | NetworkedPlayer sourcePlayer = GetPlayer(receivedEvent.senderId);
734 | if (sourcePlayer.isLocalPlayer)
735 | {
736 | return;
737 | }
738 | Observable observableObj;
739 | int observableIndex = receivedEvent.integerData[1];
740 | observableObj = sourcePlayer.observer.observables.Find(observer => observer.observableIndex == observableIndex);
741 | if (observableObj == null)
742 | {
743 | Debug.LogError("No observer found with this id " + receivedEvent.integerData[1]);
744 | return;
745 | }
746 | switch (receivedEvent.integerData[0])
747 | {
748 | case (int)SyncOptions.SyncPosition:
749 | observableObj.observeredTransform.transform.position = Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 0);
750 | break;
751 | case (int)SyncOptions.SyncRotation:
752 | observableObj.observeredTransform.transform.rotation = Quaternion.Euler(Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 0));
753 | break;
754 | case (int)SyncOptions.SyncPositionAndRotation:
755 | observableObj.observeredTransform.transform.position = Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 0);
756 | observableObj.observeredTransform.transform.rotation = Quaternion.Euler(Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 3));
757 | break;
758 | case (int)SyncOptions.SyncLocalPosition:
759 | Util.SetLocalPostion(observableObj.observeredTransform.transform, Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 0));
760 | break;
761 | case (int)SyncOptions.SyncLocalRotation:
762 | Util.SetLocalRotation(observableObj.observeredTransform.transform, Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 0));
763 | break;
764 | case (int)SyncOptions.SyncLocalPositionAndRotation:
765 | Util.SetLocalPostion(observableObj.observeredTransform.transform, Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 0));
766 | Util.SetLocalRotation(observableObj.observeredTransform.transform, Util.ConvertFloatArrayToVector3(receivedEvent.floatData, 3));
767 | break;
768 | }
769 | }
770 |
771 | void ReflectEvent(GamePlayEvent receivedEvent)
772 | {
773 | EdgeMultiplayCallbacks.eventReceived(receivedEvent);
774 | }
775 |
776 | void CreatePlayers(Player[] gamePlayers)
777 | {
778 | try
779 | {
780 | foreach (Player player in gamePlayers)
781 | {
782 | NetworkedPlayer networkedPlayer = SpawnPlayer(player);
783 | currentRoomPlayers.Add(networkedPlayer);
784 | EdgeMultiplayCallbacks.eventReceived += networkedPlayer.OnWebSocketEventReceived;
785 | EdgeMultiplayCallbacks.udpEventReceived += networkedPlayer.OnUDPEventReceived;
786 | if (player.playerId == gameSession.playerId)
787 | {
788 | localPlayer = MessageSender = networkedPlayer;
789 | }
790 | }
791 | }
792 | catch (NullReferenceException)
793 | {
794 | throw new Exception("EdgeMultiplay: Error in creating players, Make sure to attach your Prefabs to EdgeManager.SpawnInfo in the inspector");
795 | }
796 | catch (ArgumentOutOfRangeException)
797 | {
798 | throw new Exception("EdgeMultiplay: Error in creating players, Make sure Size of EdgeManager Spawn Info equal or greater than number of players in the room");
799 | }
800 | catch (Exception)
801 | {
802 | throw new Exception("EdgeMultiplay: Error in creating players");
803 | }
804 | }
805 | NetworkedPlayer SpawnPlayer(Player player)
806 | {
807 | GameObject playerObj = SpawnPrefabs[player.playerAvatar].gameObject;
808 | playerObj.GetComponent().SetUpPlayer(player, gameSession.roomId, player.playerId == gameSession.playerId);
809 |
810 | GameObject playerCreated;
811 | if (WorldOriginTransform != null)
812 | {
813 | playerCreated = Instantiate(playerObj, WorldOriginTransform);
814 | Util.SetLocalPostion(playerCreated.transform, SpawnInfo[player.playerIndex].position);
815 | Util.SetLocalRotation(playerCreated.transform, SpawnInfo[player.playerIndex].rotation);
816 | }
817 | else
818 | {
819 | playerCreated = Instantiate(playerObj, SpawnInfo[player.playerIndex].position, Quaternion.Euler(SpawnInfo[player.playerIndex].rotation));
820 | }
821 | NetworkedPlayer networkedPlayer = playerCreated.GetComponent();
822 | if (player.playerName == "")
823 | {
824 | playerCreated.name = networkedPlayer.playerName = "Player " + (player.playerIndex + 1);
825 | }
826 | else
827 | {
828 | playerCreated.name = player.playerName;
829 | }
830 |
831 | return networkedPlayer;
832 | }
833 | #endregion
834 |
835 | }
836 | }
837 |
--------------------------------------------------------------------------------