Skip to content

Commit

Permalink
Base Multiplayer implementation (#578)
Browse files Browse the repository at this point in the history
* Base Photon files (#516)

* add project config

* gitignore rest of photon

* Use secrets file for photon key

* Ignore photon on dotnet-format

* Update meta mr to test

* Connect photon via secrets key

* Test transient player rig

* proof of concept sync using OnInput

* Streamlined player head sync

* Scene Understanding as Guides

* Turn off walls temporarily

* rename prefab

* WIP anchors

* Getting Oculus ID

* Set and retrieve spatial anchor, reposition

* Add Meta platform SDK

* Sync position relative to open brush scene

* WIP syncing users

* Set test room

* Sync anchors across network

* Try inverse pose

* Hacky transform math

* maths learnt!

* Sync transient pointer for remote players

* tidyup

* HMDMesh prefab

* First pass brush sync!

* Use Photon struct to send strokes

* Network undo/redo stack

Will fall out of sync if clients didn't each join before someone's stack started

* WIP Erase

* Command queue, working erase

* Code tidyup

* Update player at correct time

* change target tag for meta sdk

* Delete conflicting meta file

* Remove oculus specific changes

* Clone the Photon repo if running from a branch, but not a fork

* clone photon in build step

* Try patch around quest 1 specifics

* missed closing xml

* fix filepath for manifest overwrite

* supportedDevices is pickier than it should be

---------

Co-authored-by: Mike Miller <[email protected]>
Co-authored-by: Andy Baker <[email protected]>
  • Loading branch information
3 people authored Dec 5, 2023
1 parent ccd8b4b commit 16b53a7
Show file tree
Hide file tree
Showing 58 changed files with 3,266 additions and 108 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ env:
UNITY_EMAIL: ${{ fromJSON(format('["[email protected]", "{0}"]', secrets.UNITY_EMAIL))[secrets.UNITY_SERIAL != null] }}
UNITY_PASSWORD: ${{ fromJSON(format('["DoNotStealThis1", "{0}"]', secrets.UNITY_PASSWORD))[secrets.UNITY_SERIAL != null] }}
UNITY_LICENSE: ${{ fromJSON('["<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><TimeStamp Value=\"chxldhvc0zh5Vw==\"/>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"C372866C-B44C-5E48-806F-7583B55F04FB\"/>\n <Binding Key=\"2\" Value=\"C02F32Y5ML85\"/>\n </MachineBindings>\n <MachineID Value=\"LcL/yxIaeUG12OSX31mKDtxcVx8=\"/>\n <SerialHash Value=\"e25c63636985259e763d40cc9253cdfe6a862ceb\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUtFWUItMzYyOC0zWEI3LVBZVVEtTUI5VQ==\"/>\n <SerialMasked Value=\"F4-KEYB-3628-3XB7-PYUQ-XXXX\"/>\n <StartDate Value=\"2023-11-21T00:00:00\"/>\n <UpdateDate Value=\"2023-11-22T06:03:23\"/>\n <InitialActivationDate Value=\"2023-11-21T06:03:21\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2017.2.0\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License><Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>9DZF11miRAzx7TIuCxih78B6CXU=</DigestValue></Reference></SignedInfo><SignatureValue>weRQubqMNN61lSZtm/e7S+UDzTPNQjM5aQl/c4aKLH/b2khefpgLfdWneoxdnNopA6+rW6kBqxWt\nhMLdHY+oAOfDsfmMQRTnmQG0Y3G3xh6gjGP1RAIHoLDfFHf+0LQ3FakA2WehcFWPSYeVDrdxm3HW\nqMmdKWooD9i+J4s4rQFTDx9+/G6yjc5KGTyGxIz3c5kxTEkV2qsFPXsauomY9Z8YPKy+cZK7g+Ol\npO+LhtzetgTIlIN/qG8eByjlp6nOuVGdDOIrhNJW+vllNyx0qNWPREadVrhFViI4UXegMFRl5gJc\nrgcrlr/fD+NorDVLfcu7D863QXkkuriILUIq2Q==</SignatureValue></Signature></root>", null]')[secrets.UNITY_SERIAL != null] }}
PHOTON_PAT: ${{ secrets.PHOTON_PAT }}
jobs:
configuration:
if: |
Expand Down Expand Up @@ -273,6 +274,8 @@ jobs:
mkdir Packages/com.unity.xr.picoxr
wget -q https://sdk.picovr.com/developer-platform/sdk/PICO%20Unity%20Integration%20SDK%20v211.zip -O package.zip
unzip package.zip -d Packages/com.unity.xr.picoxr
# Pico has a GUID conflict because they copied code from Oculus. Delete the offending .meta file from our mutable Pico SDK.
rm Packages/com.unity.xr.picoxr/Platform/Scripts/Models/Common.cs.meta
- name: Install TextMesh Pro package
run: |
Expand All @@ -296,6 +299,21 @@ jobs:
cp -R 'tmp.package/Assets/TextMesh Pro' Assets/
rm -rf tmp.plugin tmp.package
- name: Checkout private photon repository if available
if: ${{ env.PHOTON_PAT }}
uses: actions/checkout@v4
with:
token: ${{ env.PHOTON_PAT }}
repository: icosa-mirror/photon-fusion
path: photon-fusion-mirror/

- name: Copy photon files
if: ${{ env.PHOTON_PAT }}
run: |
rsync -a --ignore-existing photon-fusion-mirror/ ./
echo "For debugging: these are the files in Assets/Photon:"
find Assets/Photon
- name: Restore Library/
id: cache_library
uses: actions/cache/restore@v3
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,10 @@
# Pico Integration
Packages/com.unity.xr.picoxr

# Photon Integration
/Assets/Photon/**
/Assets/Photon.meta
!/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion*

# Cache files
*.cache
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ repos:
rev: "a2c1431dabf2229052cf598f86f31bc3eac5bc7b" # Main as of 2022-12-05
hooks:
- id: dotnet-format
exclude: ^(Assets/ThirdParty)|(Packages/)
exclude: ^(Assets/ThirdParty)|(Packages/)|(Assets/Photon/)
entry: dotnet format whitespace
args:
- --folder
Expand Down
113 changes: 113 additions & 0 deletions Assets/Editor/BuildTiltBrushPostProcess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2023 The Open Brush Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.IO;
using System.Xml;
using UnityEditor;
#if UNITY_ANDROID
using UnityEditor.Android;
#endif

[InitializeOnLoad]
public class BuildTiltBrushPostProcess
#if UNITY_ANDROID
: IPostGenerateGradleAndroidProject
#endif
{
// OVRGradleGeneration is 99999, so we'll just go to the extreme.
public int callbackOrder => 1000000;

public void OnPostGenerateGradleAndroidProject(string path)
{
string manifestFolder = Path.Combine(path, "src/main");
string file = manifestFolder + "/AndroidManifest.xml";

try
{
XmlDocument doc = new XmlDocument();
doc.Load(file);

XmlElement element = (XmlElement)doc.SelectSingleNode("/manifest");
var androidNamespaceURI = element.GetAttribute("xmlns:android");



UnityEngine.Debug.Log("Add all quest supported devices.");
AddOrRemoveTag(doc,
androidNamespaceURI,
"/manifest/application",
"meta-data",
"com.oculus.supportedDevices",
true,
true,
"value", "quest|quest2"
);

doc.Save(file);
}
catch (System.Exception e)
{
UnityEngine.Debug.LogException(e);
}
}

private static void AddOrRemoveTag(XmlDocument doc, string @namespace, string path, string elementName, string name,
bool required, bool modifyIfFound, params string[] attrs) // name, value pairs
{
var nodes = doc.SelectNodes(path + "/" + elementName);
XmlElement element = null;
foreach (XmlElement e in nodes)
{
if (name == null || name == e.GetAttribute("name", @namespace))
{
element = e;
break;
}
}

if (required)
{
if (element == null)
{
var parent = doc.SelectSingleNode(path);
element = doc.CreateElement(elementName);
element.SetAttribute("name", @namespace, name);
parent.AppendChild(element);
}

for (int i = 0; i < attrs.Length; i += 2)
{
if (modifyIfFound || string.IsNullOrEmpty(element.GetAttribute(attrs[i], @namespace)))
{
if (attrs[i + 1] != null)
{
element.SetAttribute(attrs[i], @namespace, attrs[i + 1]);
}
else
{
element.RemoveAttribute(attrs[i], @namespace);
}
}
}
}
else
{
if (element != null && modifyIfFound)
{
element.ParentNode.RemoveChild(element);
}
}
}

}
11 changes: 11 additions & 0 deletions Assets/Editor/BuildTiltBrushPostProcess.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions Assets/Oculus/OculusProjectConfig.asset
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,26 @@ MonoBehaviour:
handTrackingSupport: 0
handTrackingFrequency: 0
handTrackingVersion: 0
anchorSupport: 0
sharedAnchorSupport: 0
multimodalHandsControllersSupport: 0
anchorSupport: 1
sharedAnchorSupport: 1
renderModelSupport: 0
trackedKeyboardSupport: 0
bodyTrackingSupport: 0
faceTrackingSupport: 0
eyeTrackingSupport: 0
virtualKeyboardSupport: 0
sceneSupport: 1
disableBackups: 0
enableNSCConfig: 0
securityXmlPath:
skipUnneededShaders: 0
focusAware: 1
requiresSystemKeyboard: 0
experimentalFeaturesEnabled: 0
insightPassthroughEnabled: 1
insightPassthroughEnabled: 0
_insightPassthroughSupport: 1
systemSplashScreen: {fileID: 0}
ovrPluginMd5Win64: cc745177e7550713731157d64af22834
ovrPluginMd5Android: 997ec6c34867e09e006518cbedd3a649
systemSplashScreenType: 0
ovrPluginMd5Win64: 67e58deb5b905ae742512bbee3e06a70
ovrPluginMd5Android: 017d859ada160e45b27588f910d5d8d8
8 changes: 8 additions & 0 deletions Assets/OculusMR.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions Assets/OculusMR/AnchorToGuide.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2023 The Open Brush Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace TiltBrush
{
public class AnchorToGuide : MonoBehaviour
{
// Start is called before the first frame update

private OVRSceneVolume m_SceneComponentVolume;
private OVRScenePlane m_SceneComponentPlane;
private OVRSemanticClassification m_Classification;

void Start()
{
m_SceneComponentVolume = GetComponent<OVRSceneVolume>();

if (m_SceneComponentVolume)
{
var dimentions = m_SceneComponentVolume.Dimensions;

var pos = App.Scene.transform.InverseTransformPoint(this.transform.position);
pos.y /= 2.0f;
var tr = TrTransform.TR(pos, this.transform.rotation);

CreateWidgetCommand createCommand = new CreateWidgetCommand(
WidgetManager.m_Instance.GetStencilPrefab(StencilType.Cube), tr, null, true);
SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand);

SketchMemoryScript.m_Instance.PerformAndRecordCommand(
new MoveWidgetCommand(createCommand.Widget, tr, dimentions * 10));

return;
}

// Hacky but quick proof of concept
// TODO: Tidyup, seems a bit broken right now.
// You end up stuck grabbin the wall planes.
// m_SceneComponentPlane = GetComponent<OVRScenePlane>();

// if(m_SceneComponentPlane)
// {
// var dimentions = m_SceneComponentPlane.Dimensions;

// var tr = TrTransform.TR(this.transform.position, this.transform.rotation);

// CreateWidgetCommand createCommand = new CreateWidgetCommand(
// WidgetManager.m_Instance.GetStencilPrefab(StencilType.Plane), tr, null, true);
// SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand);

// SketchMemoryScript.m_Instance.PerformAndRecordCommand(
// new MoveWidgetCommand(createCommand.Widget, tr, dimentions * 10));

// return;
// }
}
}

}
11 changes: 11 additions & 0 deletions Assets/OculusMR/AnchorToGuide.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 82 additions & 0 deletions Assets/OculusMR/OculusMR.prefab
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7081236046508280627
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7809753190829035278}
- component: {fileID: 5619048699234477578}
- component: {fileID: -3473970634249864913}
- component: {fileID: 5425681579648101261}
m_Layer: 0
m_Name: OculusMR
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7809753190829035278
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7081236046508280627}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5619048699234477578
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7081236046508280627}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 57acf6fd80bfa99419bc6274bd16c6c8, type: 3}
m_Name:
m_EditorClassIdentifier:
ovrSceneManager: {fileID: 5425681579648101261}
m_SpatialAnchorManager: {fileID: -3473970634249864913}
--- !u!114 &-3473970634249864913
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7081236046508280627}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 91e9b9159d619a2428162e46f16afb16, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &5425681579648101261
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7081236046508280627}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f2253d75e3ddc644b8153409c62dd74e, type: 3}
m_Name:
m_EditorClassIdentifier:
PlanePrefab: {fileID: 5531902977439992835, guid: 96517f8af456f7943b86d2b7ed027964,
type: 3}
VolumePrefab: {fileID: 5531902977439992835, guid: 96517f8af456f7943b86d2b7ed027964,
type: 3}
PrefabOverrides: []
VerboseLogging: 0
MaxSceneAnchorUpdatesPerFrame: 3
_initialAnchorParent: {fileID: 0}
Loading

0 comments on commit 16b53a7

Please sign in to comment.