EVERYTHING
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
using System.Collections.Generic;
|
||||
using BlockSpace.Voxels;
|
||||
using Photon.Pun;
|
||||
using Photon.VR;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class CreatorFlightController : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private float flySpeed = 3.5f;
|
||||
|
||||
[SerializeField]
|
||||
private float deadZone = 0.15f;
|
||||
|
||||
private InputDevice leftHand;
|
||||
|
||||
private bool flightProvidersDisabled;
|
||||
|
||||
private readonly Dictionary<Behaviour, bool> cachedProviderStates = new Dictionary<Behaviour, bool>();
|
||||
|
||||
private CharacterController phasedController;
|
||||
|
||||
private bool controllerCollisionCached;
|
||||
|
||||
private bool cachedControllerEnabled;
|
||||
|
||||
private bool cachedControllerDetectCollisions;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
leftHand = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
RestoreProviders();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!PhotonNetwork.InRoom)
|
||||
{
|
||||
RestoreProviders();
|
||||
return;
|
||||
}
|
||||
VoxelGroupManager instance = VoxelGroupManager.Instance;
|
||||
if (instance == null || instance.EditingLocked || !instance.CreatorMode)
|
||||
{
|
||||
RestoreProviders();
|
||||
RestoreCharacterControllerCollisions();
|
||||
return;
|
||||
}
|
||||
DisableNonFlightProviders();
|
||||
if (!leftHand.isValid)
|
||||
{
|
||||
leftHand = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
|
||||
}
|
||||
Vector2 value = Vector2.zero;
|
||||
if (leftHand.isValid)
|
||||
{
|
||||
leftHand.TryGetFeatureValue(CommonUsages.primary2DAxis, out value);
|
||||
}
|
||||
if (Mathf.Abs(value.x) < deadZone)
|
||||
{
|
||||
value.x = 0f;
|
||||
}
|
||||
if (Mathf.Abs(value.y) < deadZone)
|
||||
{
|
||||
value.y = 0f;
|
||||
}
|
||||
Transform transform = ((PhotonVRManager.Manager != null) ? PhotonVRManager.Manager.Head : null);
|
||||
Transform localVrTeleportRoot = PlayerTeleportUtil.GetLocalVrTeleportRoot();
|
||||
if (!(transform == null) && !(localVrTeleportRoot == null))
|
||||
{
|
||||
Vector3 obj = ((transform.forward.sqrMagnitude > 0.0001f) ? transform.forward.normalized : Vector3.forward);
|
||||
Vector3 vector = ((transform.right.sqrMagnitude > 0.0001f) ? transform.right.normalized : Vector3.right);
|
||||
Vector3 vector2 = obj * value.y + vector * value.x;
|
||||
if (vector2.sqrMagnitude > 1f)
|
||||
{
|
||||
vector2.Normalize();
|
||||
}
|
||||
CharacterController component = localVrTeleportRoot.GetComponent<CharacterController>();
|
||||
if (component != null)
|
||||
{
|
||||
ApplyCharacterControllerPhase(component);
|
||||
}
|
||||
Vector3 vector3 = vector2 * flySpeed;
|
||||
if (!(vector3.sqrMagnitude <= 0.0001f))
|
||||
{
|
||||
localVrTeleportRoot.position += vector3 * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableNonFlightProviders()
|
||||
{
|
||||
if (flightProvidersDisabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PhotonVRManager manager = PhotonVRManager.Manager;
|
||||
if (!(manager == null))
|
||||
{
|
||||
cachedProviderStates.Clear();
|
||||
cachedProviderStates.Clear();
|
||||
DisableBehavioursUnder(manager.gameObject);
|
||||
if (manager.LocalPlayer != null)
|
||||
{
|
||||
DisableBehavioursUnder(manager.LocalPlayer.gameObject);
|
||||
}
|
||||
if (manager.transform.root != null)
|
||||
{
|
||||
DisableBehavioursUnder(manager.transform.root.gameObject);
|
||||
}
|
||||
flightProvidersDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableBehavioursUnder(GameObject root)
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Behaviour[] componentsInChildren = root.GetComponentsInChildren<Behaviour>(includeInactive: true);
|
||||
foreach (Behaviour behaviour in componentsInChildren)
|
||||
{
|
||||
if (!(behaviour == null) && !(behaviour == this))
|
||||
{
|
||||
string text = behaviour.GetType().Name;
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("TurnProvider") && !text.Contains("SmoothTurn") && !text.Contains("SnapTurn") && (text.Contains("GravityProvider") || text.Contains("MoveProvider") || text.Contains("LocomotionProvider") || text.Contains("TeleportationProvider") || text.Contains("ClimbProvider") || text.Contains("ContinuousMove") || text.Contains("DynamicMove")))
|
||||
{
|
||||
cachedProviderStates[behaviour] = behaviour.enabled;
|
||||
behaviour.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreProviders()
|
||||
{
|
||||
if (!flightProvidersDisabled)
|
||||
{
|
||||
RestoreCharacterControllerCollisions();
|
||||
return;
|
||||
}
|
||||
foreach (KeyValuePair<Behaviour, bool> cachedProviderState in cachedProviderStates)
|
||||
{
|
||||
if (cachedProviderState.Key != null)
|
||||
{
|
||||
cachedProviderState.Key.enabled = cachedProviderState.Value;
|
||||
}
|
||||
}
|
||||
cachedProviderStates.Clear();
|
||||
flightProvidersDisabled = false;
|
||||
RestoreCharacterControllerCollisions();
|
||||
}
|
||||
|
||||
private void ApplyCharacterControllerPhase(CharacterController controller)
|
||||
{
|
||||
if (!(controller == null))
|
||||
{
|
||||
if (phasedController != controller)
|
||||
{
|
||||
RestoreCharacterControllerCollisions();
|
||||
phasedController = controller;
|
||||
cachedControllerEnabled = controller.enabled;
|
||||
cachedControllerDetectCollisions = controller.detectCollisions;
|
||||
controllerCollisionCached = true;
|
||||
}
|
||||
controller.enabled = false;
|
||||
controller.detectCollisions = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreCharacterControllerCollisions()
|
||||
{
|
||||
if (controllerCollisionCached && phasedController != null)
|
||||
{
|
||||
phasedController.enabled = cachedControllerEnabled;
|
||||
phasedController.detectCollisions = cachedControllerDetectCollisions;
|
||||
EnsureControllerUsableAfterEdit(phasedController);
|
||||
}
|
||||
phasedController = null;
|
||||
controllerCollisionCached = false;
|
||||
}
|
||||
|
||||
private static void EnsureControllerUsableAfterEdit(CharacterController controller)
|
||||
{
|
||||
if (!(controller == null))
|
||||
{
|
||||
controller.enabled = true;
|
||||
controller.detectCollisions = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ce56b9c525bcb892a477ee43d16186d
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using Photon.Pun;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class FallResetTeleport : MonoBehaviourPunCallbacks
|
||||
{
|
||||
[SerializeField]
|
||||
private float yKillPlane = -2000f;
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 respawnPosition = new Vector3(0f, 5f, 0f);
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null)
|
||||
{
|
||||
Transform localVrTeleportRoot = PlayerTeleportUtil.GetLocalVrTeleportRoot();
|
||||
Vector3 obj = ((localVrTeleportRoot != null) ? localVrTeleportRoot.position : base.transform.position);
|
||||
if (!(obj.y >= yKillPlane))
|
||||
{
|
||||
PlayerTeleportUtil.TryTeleportLocalPlayer(respawnPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2504ffd6bd641f2cc6b5297611d2e601
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Photon.Pun;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class LobbyOnlyEnabler : MonoBehaviourPunCallbacks
|
||||
{
|
||||
[Tooltip("Objects that should only be enabled while in the Lobby world (Lobby or Lobby|###).")]
|
||||
[SerializeField]
|
||||
private GameObject[] targets;
|
||||
|
||||
[Tooltip("If true, toggles this object's children too (but keeps this GameObject active so the script still runs).")]
|
||||
[SerializeField]
|
||||
private bool includeSelf;
|
||||
|
||||
[Tooltip("World base name considered the lobby.")]
|
||||
[SerializeField]
|
||||
private string lobbyWorldName = "Lobby";
|
||||
|
||||
[Tooltip("How often to check the lobby status in seconds.")]
|
||||
[SerializeField]
|
||||
private float checkInterval = 0.5f;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
InvokeRepeating("Apply", 0f, checkInterval);
|
||||
Apply();
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
bool active = IsInLobbyWorld();
|
||||
if (includeSelf)
|
||||
{
|
||||
for (int i = 0; i < base.transform.childCount; i++)
|
||||
{
|
||||
Transform child = base.transform.GetChild(i);
|
||||
if (child != null)
|
||||
{
|
||||
child.gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targets == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int j = 0; j < targets.Length; j++)
|
||||
{
|
||||
GameObject gameObject = targets[j];
|
||||
if (!(gameObject == null) && !(gameObject == base.gameObject))
|
||||
{
|
||||
gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInLobbyWorld()
|
||||
{
|
||||
if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string baseWorldName = GetBaseWorldName(PhotonNetwork.CurrentRoom.Name ?? string.Empty);
|
||||
string text = (lobbyWorldName ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
text = "Lobby";
|
||||
}
|
||||
return string.Equals(baseWorldName, text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string GetBaseWorldName(string roomName)
|
||||
{
|
||||
string text = (roomName ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
int num = text.LastIndexOf('|');
|
||||
if (num <= 0)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
string text2 = text.Substring(num + 1);
|
||||
if (text2.Length == 3 && Regex.IsMatch(text2, "^[A-Za-z0-9]{3}$"))
|
||||
{
|
||||
return text.Substring(0, num);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 475e38d91f56973ad2a1e19118f4433c
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
using Photon.Pun;
|
||||
using Photon.VR;
|
||||
using Photon.VR.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
public static class PlayerTeleportUtil
|
||||
{
|
||||
public static Transform GetLocalVrTeleportRoot()
|
||||
{
|
||||
if (PhotonVRManager.Manager == null || PhotonVRManager.Manager.Head == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Transform head = PhotonVRManager.Manager.Head;
|
||||
if (head.parent == null)
|
||||
{
|
||||
return head;
|
||||
}
|
||||
Transform parent = head.parent;
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent.GetComponent<CharacterController>() != null)
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
if (parent.parent == null)
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
public static bool TryTeleportLocalPlayer(Vector3 position)
|
||||
{
|
||||
Transform localVrTeleportRoot = GetLocalVrTeleportRoot();
|
||||
if (localVrTeleportRoot != null)
|
||||
{
|
||||
TeleportAndZeroVelocity(localVrTeleportRoot.gameObject, position);
|
||||
return true;
|
||||
}
|
||||
PhotonVRPlayer photonVRPlayer = ((PhotonVRManager.Manager != null) ? PhotonVRManager.Manager.LocalPlayer : null);
|
||||
if (photonVRPlayer != null)
|
||||
{
|
||||
TeleportAndZeroVelocity(photonVRPlayer.gameObject, position);
|
||||
return true;
|
||||
}
|
||||
int num = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0);
|
||||
PhotonVRPlayer[] array = Object.FindObjectsByType<PhotonVRPlayer>(FindObjectsInactive.Include);
|
||||
foreach (PhotonVRPlayer photonVRPlayer2 in array)
|
||||
{
|
||||
if (!(photonVRPlayer2 == null))
|
||||
{
|
||||
PhotonView component = photonVRPlayer2.GetComponent<PhotonView>();
|
||||
if (!(component == null) && (num <= 0 || component.OwnerActorNr == num) && (num > 0 || component.IsMine))
|
||||
{
|
||||
TeleportAndZeroVelocity(photonVRPlayer2.gameObject, position);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void TeleportAndZeroVelocity(GameObject root, Vector3 position)
|
||||
{
|
||||
if (!(root == null))
|
||||
{
|
||||
CharacterController component = root.GetComponent<CharacterController>();
|
||||
if (component != null)
|
||||
{
|
||||
bool enabled = component.enabled;
|
||||
component.enabled = false;
|
||||
root.transform.position = position;
|
||||
component.enabled = enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
root.transform.position = position;
|
||||
}
|
||||
Rigidbody component2 = root.GetComponent<Rigidbody>();
|
||||
if (component2 != null)
|
||||
{
|
||||
component2.linearVelocity = Vector3.zero;
|
||||
component2.angularVelocity = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 638c6756de7d84b6b5b83f87f8d8baa1
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,390 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
public sealed class WorldBackendClient
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class ResolveWorldResponse
|
||||
{
|
||||
public bool ok;
|
||||
|
||||
public bool exists;
|
||||
|
||||
public string worldId;
|
||||
|
||||
public string name;
|
||||
|
||||
public string visibility;
|
||||
|
||||
public bool isCreator;
|
||||
|
||||
public string updatedAt;
|
||||
|
||||
public string error;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class LobbyTargetResponse
|
||||
{
|
||||
public bool ok;
|
||||
|
||||
public string worldName;
|
||||
|
||||
public string worldId;
|
||||
|
||||
public string worldVersion;
|
||||
|
||||
public string roomName;
|
||||
|
||||
public string action;
|
||||
|
||||
public bool exists;
|
||||
|
||||
public int roomsSeen;
|
||||
|
||||
public string error;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class VisitResponse
|
||||
{
|
||||
public bool ok;
|
||||
|
||||
public int visits;
|
||||
|
||||
public string error;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class BroadcastReloadResponse
|
||||
{
|
||||
public bool ok;
|
||||
|
||||
public int targets;
|
||||
|
||||
public string error;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class UploadGroupsResponse
|
||||
{
|
||||
public bool ok;
|
||||
|
||||
public bool saved;
|
||||
|
||||
public int bytes;
|
||||
|
||||
public string updatedAt;
|
||||
|
||||
public string error;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private sealed class BroadcastReloadRequest
|
||||
{
|
||||
public string playerId;
|
||||
|
||||
public string worldName;
|
||||
}
|
||||
|
||||
private readonly string baseUrl;
|
||||
|
||||
public WorldBackendClient(string baseUrl)
|
||||
{
|
||||
this.baseUrl = (baseUrl ?? string.Empty).Trim().TrimEnd('/');
|
||||
}
|
||||
|
||||
public IEnumerator ResolveLobbyTarget(string worldName, int maxPlayers, Action<LobbyTargetResponse> onDone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
onDone?.Invoke(new LobbyTargetResponse
|
||||
{
|
||||
ok = false,
|
||||
error = "No base URL set."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string text = (worldName ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onDone?.Invoke(new LobbyTargetResponse
|
||||
{
|
||||
ok = false,
|
||||
error = "World name is required."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string uri = baseUrl + "/api/worlds/lobby-target?name=" + UnityWebRequest.EscapeURL(text) + "&maxPlayers=" + Mathf.Clamp(maxPlayers, 1, 255);
|
||||
using UnityWebRequest request = UnityWebRequest.Get(uri);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
yield return request.SendWebRequest();
|
||||
LobbyTargetResponse lobbyTargetResponse = null;
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
lobbyTargetResponse = JsonUtility.FromJson<LobbyTargetResponse>(request.downloadHandler.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
if (lobbyTargetResponse == null)
|
||||
{
|
||||
lobbyTargetResponse = new LobbyTargetResponse
|
||||
{
|
||||
ok = false,
|
||||
error = request.error
|
||||
};
|
||||
}
|
||||
onDone?.Invoke(lobbyTargetResponse);
|
||||
}
|
||||
|
||||
public IEnumerator ResolveWorld(string roomName, string playerId, Action<ResolveWorldResponse> onDone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
onDone?.Invoke(new ResolveWorldResponse
|
||||
{
|
||||
ok = false,
|
||||
exists = false,
|
||||
error = "No base URL set."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string text = (roomName ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onDone?.Invoke(new ResolveWorldResponse
|
||||
{
|
||||
ok = false,
|
||||
exists = false,
|
||||
error = "Room name is required."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string uri = baseUrl + "/api/worlds/resolve?name=" + UnityWebRequest.EscapeURL(text) + "&playerId=" + UnityWebRequest.EscapeURL(playerId ?? string.Empty);
|
||||
using UnityWebRequest request = UnityWebRequest.Get(uri);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
yield return request.SendWebRequest();
|
||||
ResolveWorldResponse resolveWorldResponse = null;
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
resolveWorldResponse = JsonUtility.FromJson<ResolveWorldResponse>(request.downloadHandler.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
if (resolveWorldResponse == null)
|
||||
{
|
||||
resolveWorldResponse = new ResolveWorldResponse
|
||||
{
|
||||
ok = false,
|
||||
exists = false,
|
||||
error = request.error
|
||||
};
|
||||
}
|
||||
onDone?.Invoke(resolveWorldResponse);
|
||||
}
|
||||
|
||||
public IEnumerator DownloadGroups(string worldId, Action<byte[]> onDone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
onDone?.Invoke(Array.Empty<byte>());
|
||||
yield break;
|
||||
}
|
||||
string text = (worldId ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onDone?.Invoke(Array.Empty<byte>());
|
||||
yield break;
|
||||
}
|
||||
string uri = baseUrl + "/api/worlds/" + UnityWebRequest.EscapeURL(text) + "/groups";
|
||||
using UnityWebRequest request = UnityWebRequest.Get(uri);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
yield return request.SendWebRequest();
|
||||
if (request.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
onDone?.Invoke(Array.Empty<byte>());
|
||||
yield break;
|
||||
}
|
||||
onDone?.Invoke(request.downloadHandler.data ?? Array.Empty<byte>());
|
||||
}
|
||||
|
||||
public IEnumerator UploadGroups(string worldId, byte[] gzipBytes, string playerId, Action<UploadGroupsResponse> onDone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
onDone?.Invoke(new UploadGroupsResponse
|
||||
{
|
||||
ok = false,
|
||||
saved = false,
|
||||
bytes = 0,
|
||||
error = "No base URL set."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string text = (worldId ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onDone?.Invoke(new UploadGroupsResponse
|
||||
{
|
||||
ok = false,
|
||||
saved = false,
|
||||
bytes = 0,
|
||||
error = "worldId is required."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string text2 = (playerId ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text2))
|
||||
{
|
||||
onDone?.Invoke(new UploadGroupsResponse
|
||||
{
|
||||
ok = false,
|
||||
saved = false,
|
||||
bytes = 0,
|
||||
error = "playerId is required."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string url = baseUrl + "/api/worlds/" + UnityWebRequest.EscapeURL(text) + "/groups?playerId=" + UnityWebRequest.EscapeURL(text2);
|
||||
byte[] data = gzipBytes ?? Array.Empty<byte>();
|
||||
using UnityWebRequest request = new UnityWebRequest(url, "PUT");
|
||||
request.uploadHandler = new UploadHandlerRaw(data);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Content-Type", "application/octet-stream");
|
||||
yield return request.SendWebRequest();
|
||||
UploadGroupsResponse uploadGroupsResponse = null;
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
uploadGroupsResponse = JsonUtility.FromJson<UploadGroupsResponse>(request.downloadHandler.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
if (uploadGroupsResponse == null)
|
||||
{
|
||||
uploadGroupsResponse = new UploadGroupsResponse
|
||||
{
|
||||
ok = false,
|
||||
saved = false,
|
||||
bytes = 0,
|
||||
error = request.error
|
||||
};
|
||||
}
|
||||
onDone?.Invoke(uploadGroupsResponse);
|
||||
}
|
||||
|
||||
public IEnumerator IncrementVisit(string worldId, Action<VisitResponse> onDone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
onDone?.Invoke(new VisitResponse
|
||||
{
|
||||
ok = false,
|
||||
visits = 0,
|
||||
error = "No base URL set."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string text = (worldId ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
onDone?.Invoke(new VisitResponse
|
||||
{
|
||||
ok = false,
|
||||
visits = 0,
|
||||
error = "worldId is required."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string url = baseUrl + "/api/worlds/" + UnityWebRequest.EscapeURL(text) + "/visit";
|
||||
using UnityWebRequest request = new UnityWebRequest(url, "POST");
|
||||
request.uploadHandler = new UploadHandlerRaw(Array.Empty<byte>());
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
yield return request.SendWebRequest();
|
||||
VisitResponse visitResponse = null;
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
visitResponse = JsonUtility.FromJson<VisitResponse>(request.downloadHandler.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
if (visitResponse == null)
|
||||
{
|
||||
visitResponse = new VisitResponse
|
||||
{
|
||||
ok = false,
|
||||
visits = 0,
|
||||
error = request.error
|
||||
};
|
||||
}
|
||||
onDone?.Invoke(visitResponse);
|
||||
}
|
||||
|
||||
public IEnumerator BroadcastReload(string playerId, string worldName, Action<BroadcastReloadResponse> onDone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(baseUrl))
|
||||
{
|
||||
onDone?.Invoke(new BroadcastReloadResponse
|
||||
{
|
||||
ok = false,
|
||||
targets = 0,
|
||||
error = "No base URL set."
|
||||
});
|
||||
yield break;
|
||||
}
|
||||
string url = baseUrl + "/api/worlds/broadcast-reload";
|
||||
string s = JsonUtility.ToJson(new BroadcastReloadRequest
|
||||
{
|
||||
playerId = (playerId ?? ""),
|
||||
worldName = (worldName ?? "")
|
||||
});
|
||||
using UnityWebRequest request = new UnityWebRequest(url, "POST");
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
||||
request.uploadHandler = new UploadHandlerRaw(bytes);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
yield return request.SendWebRequest();
|
||||
BroadcastReloadResponse broadcastReloadResponse = null;
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
broadcastReloadResponse = JsonUtility.FromJson<BroadcastReloadResponse>(request.downloadHandler.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
if (broadcastReloadResponse == null)
|
||||
{
|
||||
broadcastReloadResponse = new BroadcastReloadResponse
|
||||
{
|
||||
ok = false,
|
||||
targets = 0,
|
||||
error = request.error
|
||||
};
|
||||
}
|
||||
onDone?.Invoke(broadcastReloadResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d028e93fd236b14d8bc5301b005b3ab
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,648 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using BlockSpace.Account;
|
||||
using BlockSpace.Voxels;
|
||||
using ExitGames.Client.Photon;
|
||||
using Photon.Pun;
|
||||
using Photon.Realtime;
|
||||
using Photon.VR;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class WorldCommandPoller : MonoBehaviourPunCallbacks
|
||||
{
|
||||
[Serializable]
|
||||
private sealed class PollRequest
|
||||
{
|
||||
public string playerId;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private sealed class PollResponse
|
||||
{
|
||||
public bool ok;
|
||||
|
||||
public Command command;
|
||||
|
||||
public string error;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private sealed class Command
|
||||
{
|
||||
public string type;
|
||||
|
||||
public string worldName;
|
||||
|
||||
public string roomName;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private sealed class PresencePingRequest
|
||||
{
|
||||
public string playerId;
|
||||
|
||||
public string roomName;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool enable = true;
|
||||
|
||||
[SerializeField]
|
||||
private string backendBaseUrl = "https://blockspacebackend-production.up.railway.app";
|
||||
|
||||
[SerializeField]
|
||||
private float pollIntervalSeconds = 2f;
|
||||
|
||||
[SerializeField]
|
||||
private float onlinePingIntervalSeconds = 5f;
|
||||
|
||||
[Header("Loading Screen")]
|
||||
[SerializeField]
|
||||
private GameObject loadingScreenObject;
|
||||
|
||||
[SerializeField]
|
||||
private Slider loadingProgressSlider;
|
||||
|
||||
[SerializeField]
|
||||
private WorldLoadingScreenController loadingScreen;
|
||||
|
||||
private const string DefaultLobbyWorldName = "Lobby";
|
||||
|
||||
private const string DefaultLobbyWorldId = "ZT5HjbaGAT5adlkvAirJ0A";
|
||||
|
||||
private const int PublicWorldJoinRetryCount = 5;
|
||||
|
||||
private const string RoomWorldVersionKey = "worldVersion";
|
||||
|
||||
private Coroutine pollCoroutine;
|
||||
|
||||
private Coroutine pingCoroutine;
|
||||
|
||||
private bool joinInProgress;
|
||||
|
||||
private bool joinResultReady;
|
||||
|
||||
private bool joinSucceeded;
|
||||
|
||||
private bool leftRoomReady;
|
||||
|
||||
private bool lobbyReady;
|
||||
|
||||
private bool startupLobbyJoinRequested;
|
||||
|
||||
private new void OnEnable()
|
||||
{
|
||||
StartLoopsIfNeeded();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Debug.Log("[WorldCommandPoller] Start: beginning lobby startup sequence.");
|
||||
StartCoroutine(JoinLobbyWhenBackendReady());
|
||||
}
|
||||
|
||||
private IEnumerator JoinLobbyWhenBackendReady()
|
||||
{
|
||||
BeginLoadingScreen(0.02f);
|
||||
float connectStart = Time.realtimeSinceStartup;
|
||||
while (PhotonNetwork.NetworkClientState != ClientState.ConnectedToMasterServer && Time.realtimeSinceStartup - connectStart < 15f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
Debug.Log($"[WorldCommandPoller] JoinLobbyWhenBackendReady: Photon state={PhotonNetwork.NetworkClientState} after wait.");
|
||||
SetLoadingProgress(0.08f);
|
||||
if (startupLobbyJoinRequested)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
string playerId = BlockSpaceAccountPrefs.GetUserFolderId();
|
||||
WorldBackendClient client = new WorldBackendClient(backendBaseUrl);
|
||||
float backendStart = Time.realtimeSinceStartup;
|
||||
while (Time.realtimeSinceStartup - backendStart < 20f)
|
||||
{
|
||||
WorldBackendClient.ResolveWorldResponse resolve = null;
|
||||
yield return client.ResolveWorld("Lobby", playerId, delegate(WorldBackendClient.ResolveWorldResponse r)
|
||||
{
|
||||
resolve = r;
|
||||
});
|
||||
if (resolve != null && resolve.ok)
|
||||
{
|
||||
Debug.Log("[WorldCommandPoller] JoinLobbyWhenBackendReady: backend resolve succeeded for Lobby.");
|
||||
SetLoadingProgress(0.14f);
|
||||
break;
|
||||
}
|
||||
if (resolve != null)
|
||||
{
|
||||
Debug.LogWarning($"[WorldCommandPoller] JoinLobbyWhenBackendReady: backend responded but resolve failed. Continuing to lobby join. ok={resolve.ok}, error={resolve.error}");
|
||||
SetLoadingProgress(0.14f);
|
||||
break;
|
||||
}
|
||||
Debug.LogWarning("[WorldCommandPoller] JoinLobbyWhenBackendReady: backend resolve returned null.");
|
||||
yield return new WaitForSecondsRealtime(1f);
|
||||
}
|
||||
startupLobbyJoinRequested = true;
|
||||
Debug.Log("[WorldCommandPoller] JoinLobbyWhenBackendReady: proceeding to join default Lobby world.");
|
||||
SetLoadingProgress(0.2f);
|
||||
yield return JoinWorldInstance("Lobby");
|
||||
yield return IncrementVisitForCurrentWorldIfMatches("Lobby");
|
||||
}
|
||||
|
||||
private IEnumerator IncrementVisitForCurrentWorldIfMatches(string baseWorldName)
|
||||
{
|
||||
if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null || !string.Equals(GetBaseWorldName(PhotonNetwork.CurrentRoom.Name), baseWorldName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId();
|
||||
if (!string.IsNullOrEmpty(userFolderId))
|
||||
{
|
||||
WorldBackendClient client = new WorldBackendClient(backendBaseUrl);
|
||||
WorldBackendClient.ResolveWorldResponse resolve = null;
|
||||
yield return client.ResolveWorld(baseWorldName, userFolderId, delegate(WorldBackendClient.ResolveWorldResponse r)
|
||||
{
|
||||
resolve = r;
|
||||
});
|
||||
if (resolve != null && resolve.ok && resolve.exists && !string.IsNullOrEmpty(resolve.worldId))
|
||||
{
|
||||
yield return client.IncrementVisit(resolve.worldId, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string RandomPrivateCode()
|
||||
{
|
||||
char c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[UnityEngine.Random.Range(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Length)];
|
||||
char c2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[UnityEngine.Random.Range(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Length)];
|
||||
char c3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[UnityEngine.Random.Range(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Length)];
|
||||
return new string(new char[3] { c, c2, c3 });
|
||||
}
|
||||
|
||||
private IEnumerator JoinPrivateWorldInstance(string worldName)
|
||||
{
|
||||
for (int attempt = 0; attempt < 10; attempt++)
|
||||
{
|
||||
string text = RandomPrivateCode();
|
||||
string roomName = worldName + "|" + text;
|
||||
yield return JoinExactRoom(roomName);
|
||||
if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && string.Equals(PhotonNetwork.CurrentRoom.Name, roomName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private new void OnDisable()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
pollCoroutine = null;
|
||||
pingCoroutine = null;
|
||||
SetLoadingScreen(visible: false, 0f);
|
||||
}
|
||||
|
||||
private void StartLoopsIfNeeded()
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
if (pollCoroutine == null)
|
||||
{
|
||||
pollCoroutine = StartCoroutine(PollLoop());
|
||||
}
|
||||
if (pingCoroutine == null)
|
||||
{
|
||||
pingCoroutine = StartCoroutine(PresencePingLoop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLoadingScreen(bool visible, float progress)
|
||||
{
|
||||
WorldLoadingScreenController worldLoadingScreenController = ResolveLoadingScreen();
|
||||
if (!(worldLoadingScreenController == null))
|
||||
{
|
||||
worldLoadingScreenController.SetLoading(visible);
|
||||
worldLoadingScreenController.SetProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginLoadingScreen(float progress)
|
||||
{
|
||||
WorldLoadingScreenController worldLoadingScreenController = ResolveLoadingScreen();
|
||||
if (!(worldLoadingScreenController == null))
|
||||
{
|
||||
worldLoadingScreenController.BeginLoading(progress);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLoadingProgress(float progress)
|
||||
{
|
||||
WorldLoadingScreenController worldLoadingScreenController = ResolveLoadingScreen();
|
||||
if (!(worldLoadingScreenController == null))
|
||||
{
|
||||
worldLoadingScreenController.SetCheckpoint(progress);
|
||||
}
|
||||
}
|
||||
|
||||
private WorldLoadingScreenController ResolveLoadingScreen()
|
||||
{
|
||||
if (loadingScreen == null)
|
||||
{
|
||||
loadingScreen = ((WorldLoadingScreenController.Instance != null) ? WorldLoadingScreenController.Instance : UnityEngine.Object.FindFirstObjectByType<WorldLoadingScreenController>());
|
||||
}
|
||||
if (loadingScreen == null && loadingScreenObject != null)
|
||||
{
|
||||
loadingScreen = base.gameObject.AddComponent<WorldLoadingScreenController>();
|
||||
}
|
||||
if (loadingScreen != null)
|
||||
{
|
||||
loadingScreen.Configure(loadingScreenObject, loadingProgressSlider);
|
||||
}
|
||||
return loadingScreen;
|
||||
}
|
||||
|
||||
private IEnumerator PresencePingLoop()
|
||||
{
|
||||
while (enable)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(Mathf.Clamp(onlinePingIntervalSeconds, 1f, 60f));
|
||||
if (!BlockSpaceAccountPrefs.IsLinked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string linkedPlayerId = BlockSpaceAccountPrefs.GetLinkedPlayerId();
|
||||
if (!string.IsNullOrEmpty(linkedPlayerId))
|
||||
{
|
||||
string url = (backendBaseUrl ?? string.Empty).Trim().TrimEnd('/') + "/api/presence/ping";
|
||||
string roomName = ((PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null) ? PhotonNetwork.CurrentRoom.Name : "");
|
||||
string s = JsonUtility.ToJson(new PresencePingRequest
|
||||
{
|
||||
playerId = linkedPlayerId,
|
||||
roomName = roomName
|
||||
});
|
||||
using UnityWebRequest request = new UnityWebRequest(url, "POST");
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
||||
request.uploadHandler = new UploadHandlerRaw(bytes);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
yield return request.SendWebRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator PollLoop()
|
||||
{
|
||||
while (enable)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(Mathf.Clamp(pollIntervalSeconds, 0.5f, 30f));
|
||||
if (!BlockSpaceAccountPrefs.IsLinked || joinInProgress)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string linkedPlayerId = BlockSpaceAccountPrefs.GetLinkedPlayerId();
|
||||
if (string.IsNullOrEmpty(linkedPlayerId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string text = (backendBaseUrl ?? string.Empty).Trim().TrimEnd('/');
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string url = text + "/api/commands/poll";
|
||||
string s = JsonUtility.ToJson(new PollRequest
|
||||
{
|
||||
playerId = linkedPlayerId
|
||||
});
|
||||
PollResponse response = null;
|
||||
using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
||||
request.uploadHandler = new UploadHandlerRaw(bytes);
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
yield return request.SendWebRequest();
|
||||
if (request.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
response = JsonUtility.FromJson<PollResponse>(request.downloadHandler.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
if (response == null || !response.ok || response.command == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!string.Equals(response.command.type, "join_world", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.Equals(response.command.type, "world_reload", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string text2 = (response.command.worldName ?? string.Empty).Trim();
|
||||
if (!string.IsNullOrEmpty(text2))
|
||||
{
|
||||
yield return JoinWorldInstance(text2);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(response.command.type, "world_force_save", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string text3 = (response.command.worldName ?? string.Empty).Trim();
|
||||
if (!string.IsNullOrEmpty(text3))
|
||||
{
|
||||
yield return ForceSaveWorld(text3);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(response.command.type, "join_room", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string text4 = (response.command.roomName ?? string.Empty).Trim();
|
||||
if (!string.IsNullOrEmpty(text4))
|
||||
{
|
||||
yield return JoinExactRoom(text4);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(response.command.type, "join_world_private", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string text5 = (response.command.worldName ?? string.Empty).Trim();
|
||||
if (!string.IsNullOrEmpty(text5))
|
||||
{
|
||||
yield return JoinPrivateWorldInstance(text5);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string text6 = (response.command.worldName ?? string.Empty).Trim();
|
||||
if (!string.IsNullOrEmpty(text6))
|
||||
{
|
||||
yield return JoinWorldInstance(text6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBaseWorldName(string roomName)
|
||||
{
|
||||
string text = (roomName ?? string.Empty).Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
int num = text.LastIndexOf('|');
|
||||
if (num <= 0)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
string text2 = text.Substring(num + 1);
|
||||
if (text2.Length == 3 && Regex.IsMatch(text2, "^[A-Za-z0-9]{3}$"))
|
||||
{
|
||||
return text.Substring(0, num);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private IEnumerator ForceSaveWorld(string baseWorldName)
|
||||
{
|
||||
if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && string.Equals(GetBaseWorldName(PhotonNetwork.CurrentRoom.Name), baseWorldName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
VoxelGroupPhotonSync voxelGroupPhotonSync = UnityEngine.Object.FindFirstObjectByType<VoxelGroupPhotonSync>();
|
||||
if (!(voxelGroupPhotonSync == null))
|
||||
{
|
||||
yield return voxelGroupPhotonSync.ForceSaveNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator JoinWorldInstance(string worldName)
|
||||
{
|
||||
Debug.Log("[WorldCommandPoller] JoinWorldInstance: starting join for world '" + worldName + "'.");
|
||||
joinInProgress = true;
|
||||
BeginLoadingScreen(0.2f);
|
||||
if (PhotonNetwork.InRoom)
|
||||
{
|
||||
SetLoadingProgress(0.24f);
|
||||
Debug.Log("[WorldCommandPoller] JoinWorldInstance: leaving current room '" + PhotonNetwork.CurrentRoom?.Name + "'.");
|
||||
leftRoomReady = false;
|
||||
PhotonNetwork.LeaveRoom();
|
||||
float start = Time.realtimeSinceStartup;
|
||||
while (!leftRoomReady && Time.realtimeSinceStartup - start < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
SetLoadingProgress(0.3f);
|
||||
Debug.Log($"[WorldCommandPoller] JoinWorldInstance: left room ready={leftRoomReady}, inRoom={PhotonNetwork.InRoom}.");
|
||||
}
|
||||
SetLoadingProgress(0.34f);
|
||||
float masterStart = Time.realtimeSinceStartup;
|
||||
while (PhotonNetwork.NetworkClientState != ClientState.ConnectedToMasterServer && Time.realtimeSinceStartup - masterStart < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
SetLoadingProgress(0.4f);
|
||||
if (PhotonNetwork.NetworkClientState != ClientState.ConnectedToMasterServer)
|
||||
{
|
||||
joinInProgress = false;
|
||||
SetLoadingScreen(visible: false, 0f);
|
||||
yield break;
|
||||
}
|
||||
WorldBackendClient client = new WorldBackendClient(backendBaseUrl);
|
||||
int maxPlayers = ((PhotonVRManager.Manager != null) ? PhotonVRManager.Manager.DefaultRoomLimit : 16);
|
||||
for (int attempt = 0; attempt < 5; attempt++)
|
||||
{
|
||||
SetLoadingProgress(0.46f);
|
||||
WorldBackendClient.LobbyTargetResponse target = null;
|
||||
yield return client.ResolveLobbyTarget(worldName, maxPlayers, delegate(WorldBackendClient.LobbyTargetResponse r)
|
||||
{
|
||||
target = r;
|
||||
});
|
||||
if (target == null || !target.ok || string.IsNullOrEmpty(target.roomName))
|
||||
{
|
||||
Debug.LogWarning(string.Format("[WorldCommandPoller] JoinWorldInstance: lobby target failed for '{0}' [attempt {1}/{2}]. error={3}", worldName, attempt + 1, 5, (target != null) ? target.error : "<null>"));
|
||||
yield return new WaitForSecondsRealtime(1f);
|
||||
continue;
|
||||
}
|
||||
joinResultReady = false;
|
||||
joinSucceeded = false;
|
||||
SetLoadingProgress(0.54f);
|
||||
ExitGames.Client.Photon.Hashtable hashtable = new ExitGames.Client.Photon.Hashtable();
|
||||
if (!string.IsNullOrEmpty(target.worldVersion))
|
||||
{
|
||||
hashtable["worldVersion"] = target.worldVersion;
|
||||
}
|
||||
RoomOptions roomOptions = new RoomOptions();
|
||||
roomOptions.IsVisible = true;
|
||||
roomOptions.IsOpen = true;
|
||||
roomOptions.MaxPlayers = (byte)Mathf.Clamp(maxPlayers, 1, 255);
|
||||
roomOptions.CustomRoomProperties = hashtable;
|
||||
roomOptions.CustomRoomPropertiesForLobby = new string[1] { "worldVersion" };
|
||||
RoomOptions roomOptions2 = roomOptions;
|
||||
Debug.Log($"[WorldCommandPoller] JoinWorldInstance: backend selected {target.action}('{target.roomName}') worldVersion='{target.worldVersion}' roomsSeen={target.roomsSeen} [attempt {attempt + 1}/{5}].");
|
||||
if (string.Equals(target.action, "join", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
SetLoadingProgress(0.6f);
|
||||
PhotonNetwork.JoinRoom(target.roomName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLoadingProgress(0.6f);
|
||||
PhotonNetwork.JoinOrCreateRoom(target.roomName, roomOptions2, null);
|
||||
}
|
||||
float start = Time.realtimeSinceStartup;
|
||||
while (!joinResultReady && Time.realtimeSinceStartup - start < 12f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
if (!joinResultReady && PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && string.Equals(PhotonNetwork.CurrentRoom.Name, target.roomName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
joinSucceeded = true;
|
||||
joinResultReady = true;
|
||||
Debug.Log("[WorldCommandPoller] JoinWorldInstance: detected successful join by current room '" + target.roomName + "' without callback.");
|
||||
}
|
||||
Debug.Log($"[WorldCommandPoller] JoinWorldInstance: room '{target.roomName}' join result ready={joinResultReady}, succeeded={joinSucceeded}, inRoom={PhotonNetwork.InRoom}, currentRoom={PhotonNetwork.CurrentRoom?.Name}.");
|
||||
if (joinSucceeded && PhotonNetwork.InRoom)
|
||||
{
|
||||
Debug.Log("[WorldCommandPoller] JoinWorldInstance: successfully joined room '" + target.roomName + "'.");
|
||||
joinInProgress = false;
|
||||
SetLoadingProgress(0.66f);
|
||||
yield break;
|
||||
}
|
||||
if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && !string.Equals(PhotonNetwork.CurrentRoom.Name, target.roomName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning("[WorldCommandPoller] JoinWorldInstance: still in different room '" + PhotonNetwork.CurrentRoom.Name + "' after attempting '" + target.roomName + "', leaving before next attempt.");
|
||||
leftRoomReady = false;
|
||||
PhotonNetwork.LeaveRoom();
|
||||
float leaveStart = Time.realtimeSinceStartup;
|
||||
while (!leftRoomReady && Time.realtimeSinceStartup - leaveStart < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
SetLoadingProgress(0.3f);
|
||||
}
|
||||
yield return new WaitForSecondsRealtime(0.5f);
|
||||
}
|
||||
joinInProgress = false;
|
||||
SetLoadingScreen(visible: false, 0f);
|
||||
}
|
||||
|
||||
private IEnumerator JoinExactRoom(string roomName)
|
||||
{
|
||||
Debug.Log("[WorldCommandPoller] JoinExactRoom: starting join for exact room '" + roomName + "'.");
|
||||
joinInProgress = true;
|
||||
BeginLoadingScreen(0.2f);
|
||||
if (PhotonNetwork.InRoom)
|
||||
{
|
||||
SetLoadingProgress(0.24f);
|
||||
Debug.Log("[WorldCommandPoller] JoinExactRoom: leaving current room '" + PhotonNetwork.CurrentRoom?.Name + "'.");
|
||||
leftRoomReady = false;
|
||||
PhotonNetwork.LeaveRoom();
|
||||
float start = Time.realtimeSinceStartup;
|
||||
while (!leftRoomReady && Time.realtimeSinceStartup - start < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
SetLoadingProgress(0.3f);
|
||||
Debug.Log($"[WorldCommandPoller] JoinExactRoom: left room ready={leftRoomReady}, inRoom={PhotonNetwork.InRoom}.");
|
||||
}
|
||||
SetLoadingProgress(0.34f);
|
||||
float masterStart = Time.realtimeSinceStartup;
|
||||
while (PhotonNetwork.NetworkClientState != ClientState.ConnectedToMasterServer && Time.realtimeSinceStartup - masterStart < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
SetLoadingProgress(0.4f);
|
||||
if (PhotonNetwork.NetworkClientState != ClientState.ConnectedToMasterServer)
|
||||
{
|
||||
joinInProgress = false;
|
||||
SetLoadingScreen(visible: false, 0f);
|
||||
yield break;
|
||||
}
|
||||
if (!PhotonNetwork.InLobby)
|
||||
{
|
||||
lobbyReady = false;
|
||||
PhotonNetwork.JoinLobby();
|
||||
float start = Time.realtimeSinceStartup;
|
||||
while (!lobbyReady && Time.realtimeSinceStartup - start < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
SetLoadingProgress(0.5f);
|
||||
if (!PhotonNetwork.InLobby)
|
||||
{
|
||||
joinInProgress = false;
|
||||
SetLoadingScreen(visible: false, 0f);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
joinResultReady = false;
|
||||
joinSucceeded = false;
|
||||
RoomOptions roomOptions = new RoomOptions
|
||||
{
|
||||
IsVisible = true,
|
||||
IsOpen = true,
|
||||
MaxPlayers = ((PhotonVRManager.Manager != null) ? ((byte)PhotonVRManager.Manager.DefaultRoomLimit) : 16)
|
||||
};
|
||||
Debug.Log("[WorldCommandPoller] JoinExactRoom: requesting JoinOrCreateRoom('" + roomName + "').");
|
||||
SetLoadingProgress(0.6f);
|
||||
PhotonNetwork.JoinOrCreateRoom(roomName, roomOptions, null);
|
||||
float waitStart = Time.realtimeSinceStartup;
|
||||
while (!joinResultReady && Time.realtimeSinceStartup - waitStart < 10f)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
Debug.Log($"[WorldCommandPoller] JoinExactRoom: room '{roomName}' join result ready={joinResultReady}, succeeded={joinSucceeded}, inRoom={PhotonNetwork.InRoom}.");
|
||||
joinInProgress = false;
|
||||
if (joinSucceeded && PhotonNetwork.InRoom)
|
||||
{
|
||||
SetLoadingProgress(0.66f);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLoadingScreen(visible: false, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLeftRoom()
|
||||
{
|
||||
leftRoomReady = true;
|
||||
base.OnLeftRoom();
|
||||
}
|
||||
|
||||
public override void OnJoinedRoom()
|
||||
{
|
||||
joinSucceeded = true;
|
||||
joinResultReady = true;
|
||||
Debug.Log("[WorldCommandPoller] OnJoinedRoom: joined room '" + PhotonNetwork.CurrentRoom?.Name + "' successfully.");
|
||||
base.OnJoinedRoom();
|
||||
}
|
||||
|
||||
public override void OnJoinedLobby()
|
||||
{
|
||||
lobbyReady = true;
|
||||
Debug.Log("[WorldCommandPoller] OnJoinedLobby: Photon lobby joined successfully.");
|
||||
base.OnJoinedLobby();
|
||||
}
|
||||
|
||||
public override void OnJoinRoomFailed(short returnCode, string message)
|
||||
{
|
||||
joinSucceeded = false;
|
||||
joinResultReady = true;
|
||||
Debug.LogWarning($"[WorldCommandPoller] OnJoinRoomFailed: code={returnCode}, message={message}, state={PhotonNetwork.NetworkClientState}.");
|
||||
base.OnJoinRoomFailed(returnCode, message);
|
||||
}
|
||||
|
||||
public override void OnCreateRoomFailed(short returnCode, string message)
|
||||
{
|
||||
joinSucceeded = false;
|
||||
joinResultReady = true;
|
||||
Debug.LogWarning($"[WorldCommandPoller] OnCreateRoomFailed: code={returnCode}, message={message}, state={PhotonNetwork.NetworkClientState}.");
|
||||
base.OnCreateRoomFailed(returnCode, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6188b95afcfc6352979a6717a50de0b
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,672 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using BlockSpace.Voxels;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
public static class WorldGroupsCodec
|
||||
{
|
||||
[Serializable]
|
||||
public struct WorldGroupRecord
|
||||
{
|
||||
public Vector3Int position;
|
||||
|
||||
public byte packedRotation;
|
||||
|
||||
public Vector3Int size;
|
||||
|
||||
public byte[] voxels;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct NodePortValueRecord
|
||||
{
|
||||
public string portName;
|
||||
|
||||
public BSValue value;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct WorldNodeRecord
|
||||
{
|
||||
public int nodeId;
|
||||
|
||||
public string definitionName;
|
||||
|
||||
public Vector3 position;
|
||||
|
||||
public Quaternion rotation;
|
||||
|
||||
public NodePortValueRecord[] inputValues;
|
||||
|
||||
public NodePortValueRecord[] outputValues;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct WorldWireRecord
|
||||
{
|
||||
public int outputNodeId;
|
||||
|
||||
public string outputPortName;
|
||||
|
||||
public int inputNodeId;
|
||||
|
||||
public string inputPortName;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct WorldSaveData
|
||||
{
|
||||
public List<WorldGroupRecord> groupRecords;
|
||||
|
||||
public List<WorldNodeRecord> nodeRecords;
|
||||
|
||||
public List<WorldWireRecord> wireRecords;
|
||||
}
|
||||
|
||||
private static readonly byte[] Magic = Encoding.ASCII.GetBytes("BSW1");
|
||||
|
||||
private static readonly byte[] NodesSectionMagic = Encoding.ASCII.GetBytes("BSN1");
|
||||
|
||||
private static readonly byte[] WiresSectionMagic = Encoding.ASCII.GetBytes("BSR1");
|
||||
|
||||
private const string GroupRuntimeWirePrefix = "__group_runtime__:";
|
||||
|
||||
private const string GroupSavedWirePrefix = "__group_saved__:";
|
||||
|
||||
public static WorldWireRecord CreateGroupRuntimeWireRecord(int groupId, int inputNodeId, string inputPortName)
|
||||
{
|
||||
return new WorldWireRecord
|
||||
{
|
||||
outputNodeId = 0,
|
||||
outputPortName = "__group_runtime__:" + Mathf.Max(0, groupId),
|
||||
inputNodeId = inputNodeId,
|
||||
inputPortName = inputPortName
|
||||
};
|
||||
}
|
||||
|
||||
public static WorldWireRecord CreateGroupSavedWireRecord(int savedGroupIndex, int inputNodeId, string inputPortName)
|
||||
{
|
||||
return new WorldWireRecord
|
||||
{
|
||||
outputNodeId = 0,
|
||||
outputPortName = "__group_saved__:" + Mathf.Max(0, savedGroupIndex),
|
||||
inputNodeId = inputNodeId,
|
||||
inputPortName = inputPortName
|
||||
};
|
||||
}
|
||||
|
||||
public static bool TryGetGroupRuntimeWireSourceId(WorldWireRecord record, out int groupId)
|
||||
{
|
||||
return TryParseGroupWireValue(record, "__group_runtime__:", out groupId);
|
||||
}
|
||||
|
||||
public static bool TryGetGroupSavedWireIndex(WorldWireRecord record, out int savedGroupIndex)
|
||||
{
|
||||
return TryParseGroupWireValue(record, "__group_saved__:", out savedGroupIndex);
|
||||
}
|
||||
|
||||
public static byte[] EncodeGzip(IEnumerable<VoxelGroup> groups)
|
||||
{
|
||||
return EncodeGzip(groups, null, null);
|
||||
}
|
||||
|
||||
public static byte[] EncodeGzip(IEnumerable<VoxelGroup> groups, IEnumerable<WorldNodeRecord> nodes, IEnumerable<WorldWireRecord> wires)
|
||||
{
|
||||
List<WorldGroupRecord> list = BuildGroupRecords(groups);
|
||||
List<WorldNodeRecord> list2 = ((nodes != null) ? new List<WorldNodeRecord>(nodes) : new List<WorldNodeRecord>());
|
||||
List<WorldWireRecord> list3 = ((wires != null) ? new List<WorldWireRecord>(wires) : new List<WorldWireRecord>());
|
||||
using MemoryStream memoryStream = new MemoryStream();
|
||||
memoryStream.Write(Magic, 0, Magic.Length);
|
||||
WriteVarUInt(memoryStream, (uint)list.Count);
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
WriteGroupRecord(memoryStream, list[i]);
|
||||
}
|
||||
if (list2.Count > 0)
|
||||
{
|
||||
memoryStream.Write(NodesSectionMagic, 0, NodesSectionMagic.Length);
|
||||
WriteVarUInt(memoryStream, (uint)list2.Count);
|
||||
for (int j = 0; j < list2.Count; j++)
|
||||
{
|
||||
WriteNodeRecord(memoryStream, list2[j]);
|
||||
}
|
||||
}
|
||||
if (list3.Count > 0)
|
||||
{
|
||||
memoryStream.Write(WiresSectionMagic, 0, WiresSectionMagic.Length);
|
||||
WriteVarUInt(memoryStream, (uint)list3.Count);
|
||||
for (int k = 0; k < list3.Count; k++)
|
||||
{
|
||||
WriteWireRecord(memoryStream, list3[k]);
|
||||
}
|
||||
}
|
||||
memoryStream.Position = 0L;
|
||||
using MemoryStream memoryStream2 = new MemoryStream();
|
||||
using (GZipStream destination = new GZipStream(memoryStream2, System.IO.Compression.CompressionLevel.Optimal, leaveOpen: true))
|
||||
{
|
||||
memoryStream.CopyTo(destination);
|
||||
}
|
||||
return memoryStream2.ToArray();
|
||||
}
|
||||
|
||||
public static bool TryDecodeGzip(byte[] gzipBytes, out List<WorldGroupRecord> records)
|
||||
{
|
||||
WorldSaveData saveData;
|
||||
bool result = TryDecodeGzip(gzipBytes, out saveData);
|
||||
records = saveData.groupRecords ?? new List<WorldGroupRecord>();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool TryParseGroupWireValue(WorldWireRecord record, string prefix, out int value)
|
||||
{
|
||||
value = 0;
|
||||
if (record.outputNodeId > 0 || string.IsNullOrEmpty(record.outputPortName) || !record.outputPortName.StartsWith(prefix, StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (int.TryParse(record.outputPortName.Substring(prefix.Length), out value))
|
||||
{
|
||||
return value >= 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryDecodeGzip(byte[] gzipBytes, out WorldSaveData saveData)
|
||||
{
|
||||
saveData = new WorldSaveData
|
||||
{
|
||||
groupRecords = new List<WorldGroupRecord>(),
|
||||
nodeRecords = new List<WorldNodeRecord>(),
|
||||
wireRecords = new List<WorldWireRecord>()
|
||||
};
|
||||
if (gzipBytes == null || gzipBytes.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
byte[] array;
|
||||
try
|
||||
{
|
||||
array = DecompressGzip(gzipBytes);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (array.Length < Magic.Length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
using MemoryStream memoryStream = new MemoryStream(array);
|
||||
byte[] array2 = new byte[Magic.Length];
|
||||
if (memoryStream.Read(array2, 0, array2.Length) != array2.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < Magic.Length; i++)
|
||||
{
|
||||
if (array2[i] != Magic[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
uint num;
|
||||
try
|
||||
{
|
||||
num = ReadVarUInt(memoryStream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (uint num2 = 0u; num2 < num; num2++)
|
||||
{
|
||||
if (!TryReadGroupRecord(memoryStream, out var record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
saveData.groupRecords.Add(record);
|
||||
}
|
||||
string sectionName;
|
||||
while (memoryStream.Position < memoryStream.Length && TryReadSectionHeader(memoryStream, out sectionName))
|
||||
{
|
||||
if (sectionName == "nodes")
|
||||
{
|
||||
if (!TryReadNodeSection(memoryStream, saveData.nodeRecords))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!(sectionName == "wires"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!TryReadWireSection(memoryStream, saveData.wireRecords))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<WorldGroupRecord> BuildGroupRecords(IEnumerable<VoxelGroup> groups)
|
||||
{
|
||||
List<WorldGroupRecord> list = new List<WorldGroupRecord>();
|
||||
if (groups == null)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
foreach (VoxelGroup group in groups)
|
||||
{
|
||||
if (!(group == null) && group.size.x > 0 && group.size.y > 0 && group.size.z > 0 && group.voxels != null)
|
||||
{
|
||||
list.Add(new WorldGroupRecord
|
||||
{
|
||||
position = group.position,
|
||||
packedRotation = group.packedRotation,
|
||||
size = group.size,
|
||||
voxels = (byte[])group.voxels.Clone()
|
||||
});
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void WriteGroupRecord(Stream stream, WorldGroupRecord record)
|
||||
{
|
||||
WriteVarInt(stream, record.position.x);
|
||||
WriteVarInt(stream, record.position.y);
|
||||
WriteVarInt(stream, record.position.z);
|
||||
stream.WriteByte(record.packedRotation);
|
||||
WriteVarUInt(stream, (uint)Mathf.Max(0, record.size.x));
|
||||
WriteVarUInt(stream, (uint)Mathf.Max(0, record.size.y));
|
||||
WriteVarUInt(stream, (uint)Mathf.Max(0, record.size.z));
|
||||
WriteVoxelsRle(stream, record.voxels);
|
||||
}
|
||||
|
||||
private static bool TryReadGroupRecord(Stream stream, out WorldGroupRecord record)
|
||||
{
|
||||
record = default(WorldGroupRecord);
|
||||
try
|
||||
{
|
||||
int x = ReadVarInt(stream);
|
||||
int y = ReadVarInt(stream);
|
||||
int z = ReadVarInt(stream);
|
||||
int num = stream.ReadByte();
|
||||
if (num < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint num2 = ReadVarUInt(stream);
|
||||
uint num3 = ReadVarUInt(stream);
|
||||
uint num4 = ReadVarUInt(stream);
|
||||
int num5 = (int)(num2 * num3 * num4);
|
||||
if (num5 < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte[] voxels = ReadVoxelsRle(stream, num5);
|
||||
record = new WorldGroupRecord
|
||||
{
|
||||
position = new Vector3Int(x, y, z),
|
||||
packedRotation = (byte)num,
|
||||
size = new Vector3Int((int)num2, (int)num3, (int)num4),
|
||||
voxels = voxels
|
||||
};
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadSectionHeader(Stream stream, out string sectionName)
|
||||
{
|
||||
sectionName = string.Empty;
|
||||
if (stream.Position + 4 > stream.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte[] array = new byte[4];
|
||||
if (stream.Read(array, 0, array.Length) != array.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (SectionMatches(array, NodesSectionMagic))
|
||||
{
|
||||
sectionName = "nodes";
|
||||
return true;
|
||||
}
|
||||
if (SectionMatches(array, WiresSectionMagic))
|
||||
{
|
||||
sectionName = "wires";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryReadNodeSection(Stream stream, List<WorldNodeRecord> records)
|
||||
{
|
||||
try
|
||||
{
|
||||
uint num = ReadVarUInt(stream);
|
||||
for (uint num2 = 0u; num2 < num; num2++)
|
||||
{
|
||||
records.Add(ReadNodeRecord(stream));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadWireSection(Stream stream, List<WorldWireRecord> records)
|
||||
{
|
||||
try
|
||||
{
|
||||
uint num = ReadVarUInt(stream);
|
||||
for (uint num2 = 0u; num2 < num; num2++)
|
||||
{
|
||||
records.Add(ReadWireRecord(stream));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteNodeRecord(Stream stream, WorldNodeRecord record)
|
||||
{
|
||||
WriteVarInt(stream, record.nodeId);
|
||||
WriteString(stream, record.definitionName);
|
||||
WriteFloat(stream, record.position.x);
|
||||
WriteFloat(stream, record.position.y);
|
||||
WriteFloat(stream, record.position.z);
|
||||
WriteFloat(stream, record.rotation.x);
|
||||
WriteFloat(stream, record.rotation.y);
|
||||
WriteFloat(stream, record.rotation.z);
|
||||
WriteFloat(stream, record.rotation.w);
|
||||
WritePortValueRecords(stream, record.inputValues);
|
||||
WritePortValueRecords(stream, record.outputValues);
|
||||
}
|
||||
|
||||
private static WorldNodeRecord ReadNodeRecord(Stream stream)
|
||||
{
|
||||
return new WorldNodeRecord
|
||||
{
|
||||
nodeId = ReadVarInt(stream),
|
||||
definitionName = ReadString(stream),
|
||||
position = new Vector3(ReadFloat(stream), ReadFloat(stream), ReadFloat(stream)),
|
||||
rotation = new Quaternion(ReadFloat(stream), ReadFloat(stream), ReadFloat(stream), ReadFloat(stream)),
|
||||
inputValues = ReadPortValueRecords(stream),
|
||||
outputValues = ReadPortValueRecords(stream)
|
||||
};
|
||||
}
|
||||
|
||||
private static void WriteWireRecord(Stream stream, WorldWireRecord record)
|
||||
{
|
||||
WriteVarInt(stream, record.outputNodeId);
|
||||
WriteString(stream, record.outputPortName);
|
||||
WriteVarInt(stream, record.inputNodeId);
|
||||
WriteString(stream, record.inputPortName);
|
||||
}
|
||||
|
||||
private static WorldWireRecord ReadWireRecord(Stream stream)
|
||||
{
|
||||
return new WorldWireRecord
|
||||
{
|
||||
outputNodeId = ReadVarInt(stream),
|
||||
outputPortName = ReadString(stream),
|
||||
inputNodeId = ReadVarInt(stream),
|
||||
inputPortName = ReadString(stream)
|
||||
};
|
||||
}
|
||||
|
||||
private static void WritePortValueRecords(Stream stream, NodePortValueRecord[] records)
|
||||
{
|
||||
if (records == null)
|
||||
{
|
||||
WriteVarUInt(stream, 0u);
|
||||
return;
|
||||
}
|
||||
WriteVarUInt(stream, (uint)records.Length);
|
||||
for (int i = 0; i < records.Length; i++)
|
||||
{
|
||||
WriteString(stream, records[i].portName);
|
||||
WriteBSValue(stream, records[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
private static NodePortValueRecord[] ReadPortValueRecords(Stream stream)
|
||||
{
|
||||
uint num = ReadVarUInt(stream);
|
||||
NodePortValueRecord[] array = new NodePortValueRecord[num];
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
array[i] = new NodePortValueRecord
|
||||
{
|
||||
portName = ReadString(stream),
|
||||
value = ReadBSValue(stream)
|
||||
};
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private static void WriteBSValue(Stream stream, BSValue value)
|
||||
{
|
||||
value = value.Normalize();
|
||||
stream.WriteByte((byte)value.Kind);
|
||||
switch (value.Kind)
|
||||
{
|
||||
case BSValueKind.Number:
|
||||
WriteDouble(stream, value.Number);
|
||||
break;
|
||||
case BSValueKind.Text:
|
||||
WriteString(stream, value.Text);
|
||||
break;
|
||||
case BSValueKind.Bool:
|
||||
stream.WriteByte((byte)(value.Bool ? 1u : 0u));
|
||||
break;
|
||||
case BSValueKind.Player:
|
||||
WriteVarInt(stream, value.PlayerId);
|
||||
break;
|
||||
case BSValueKind.Group:
|
||||
WriteVarInt(stream, value.GroupId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static BSValue ReadBSValue(Stream stream)
|
||||
{
|
||||
return (BSValueKind)stream.ReadByte() switch
|
||||
{
|
||||
BSValueKind.Number => BSValue.FromNumber(ReadDouble(stream)),
|
||||
BSValueKind.Text => BSValue.FromText(ReadString(stream)),
|
||||
BSValueKind.Bool => BSValue.FromBool(stream.ReadByte() != 0),
|
||||
BSValueKind.Player => BSValue.FromPlayer(ReadVarInt(stream)),
|
||||
BSValueKind.Group => BSValue.FromGroup(ReadVarInt(stream)),
|
||||
_ => BSValue.None,
|
||||
};
|
||||
}
|
||||
|
||||
private static bool SectionMatches(byte[] a, byte[] b)
|
||||
{
|
||||
if (a == null || b == null || a.Length != b.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
if (a[i] != b[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void WriteVoxelsRle(Stream stream, byte[] voxels)
|
||||
{
|
||||
if (voxels == null || voxels.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int j;
|
||||
for (int i = 0; i < voxels.Length; i += j)
|
||||
{
|
||||
byte b = voxels[i];
|
||||
j = 1;
|
||||
for (int num = voxels.Length - i; j < num && voxels[i + j] == b; j++)
|
||||
{
|
||||
}
|
||||
stream.WriteByte(b);
|
||||
WriteVarUInt(stream, (uint)j);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] ReadVoxelsRle(Stream stream, int expectedLength)
|
||||
{
|
||||
if (expectedLength <= 0)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
byte[] array = new byte[expectedLength];
|
||||
int num3;
|
||||
for (int i = 0; i < expectedLength; i += num3)
|
||||
{
|
||||
int num = stream.ReadByte();
|
||||
if (num < 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
uint num2 = ReadVarUInt(stream);
|
||||
if (num2 == 0)
|
||||
{
|
||||
throw new InvalidDataException("RLE run length was 0.");
|
||||
}
|
||||
int val = expectedLength - i;
|
||||
num3 = (int)Math.Min(num2, (uint)val);
|
||||
for (int j = 0; j < num3; j++)
|
||||
{
|
||||
array[i + j] = (byte)num;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private static byte[] DecompressGzip(byte[] gzipBytes)
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream(gzipBytes);
|
||||
using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress);
|
||||
using MemoryStream memoryStream = new MemoryStream();
|
||||
gZipStream.CopyTo(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
private static void WriteString(Stream stream, string value)
|
||||
{
|
||||
string s = value ?? string.Empty;
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
||||
WriteVarUInt(stream, (uint)bytes.Length);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
private static string ReadString(Stream stream)
|
||||
{
|
||||
uint num = ReadVarUInt(stream);
|
||||
if (num == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
byte[] array = new byte[num];
|
||||
if (stream.Read(array, 0, array.Length) != array.Length)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
return Encoding.UTF8.GetString(array);
|
||||
}
|
||||
|
||||
private static void WriteFloat(Stream stream, float value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
private static float ReadFloat(Stream stream)
|
||||
{
|
||||
byte[] array = new byte[4];
|
||||
if (stream.Read(array, 0, array.Length) != array.Length)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
return BitConverter.ToSingle(array, 0);
|
||||
}
|
||||
|
||||
private static void WriteDouble(Stream stream, double value)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
private static double ReadDouble(Stream stream)
|
||||
{
|
||||
byte[] array = new byte[8];
|
||||
if (stream.Read(array, 0, array.Length) != array.Length)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
return BitConverter.ToDouble(array, 0);
|
||||
}
|
||||
|
||||
private static void WriteVarInt(Stream stream, int value)
|
||||
{
|
||||
uint value2 = (uint)((value << 1) ^ (value >> 31));
|
||||
WriteVarUInt(stream, value2);
|
||||
}
|
||||
|
||||
private static int ReadVarInt(Stream stream)
|
||||
{
|
||||
uint num = ReadVarUInt(stream);
|
||||
return (int)((num >> 1) ^ (0 - (num & 1)));
|
||||
}
|
||||
|
||||
private static void WriteVarUInt(Stream stream, uint value)
|
||||
{
|
||||
while (value >= 128)
|
||||
{
|
||||
stream.WriteByte((byte)(value | 0x80));
|
||||
value >>= 7;
|
||||
}
|
||||
stream.WriteByte((byte)value);
|
||||
}
|
||||
|
||||
private static uint ReadVarUInt(Stream stream)
|
||||
{
|
||||
uint num = 0u;
|
||||
for (int i = 0; i < 35; i += 7)
|
||||
{
|
||||
int num2 = stream.ReadByte();
|
||||
if (num2 < 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
num |= (uint)((num2 & 0x7F) << i);
|
||||
if ((num2 & 0x80) == 0)
|
||||
{
|
||||
return num;
|
||||
}
|
||||
}
|
||||
throw new InvalidDataException("VarUInt was too long.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06756f42563711c56a7dd62fd0f60a5c
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,355 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BlockSpace.Worlds
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class WorldLoadingScreenController : MonoBehaviour
|
||||
{
|
||||
private struct CameraState
|
||||
{
|
||||
public CameraClearFlags clearFlags;
|
||||
|
||||
public Color backgroundColor;
|
||||
}
|
||||
|
||||
private struct RigidbodyState
|
||||
{
|
||||
public Vector3 linearVelocity;
|
||||
|
||||
public Vector3 angularVelocity;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private GameObject loadingScreenObject;
|
||||
|
||||
[SerializeField]
|
||||
private Slider progressSlider;
|
||||
|
||||
[SerializeField]
|
||||
private bool hideSceneWhileLoading = true;
|
||||
|
||||
[SerializeField]
|
||||
private int requiredTeleportsToComplete = 2;
|
||||
|
||||
private readonly Dictionary<Renderer, bool> rendererStates = new Dictionary<Renderer, bool>();
|
||||
|
||||
private readonly Dictionary<Canvas, bool> canvasStates = new Dictionary<Canvas, bool>();
|
||||
|
||||
private readonly Dictionary<Graphic, bool> graphicStates = new Dictionary<Graphic, bool>();
|
||||
|
||||
private readonly Dictionary<Camera, CameraState> cameraStates = new Dictionary<Camera, CameraState>();
|
||||
|
||||
private readonly Dictionary<Behaviour, bool> movementBehaviourStates = new Dictionary<Behaviour, bool>();
|
||||
|
||||
private readonly Dictionary<Rigidbody, RigidbodyState> rigidbodyStates = new Dictionary<Rigidbody, RigidbodyState>();
|
||||
|
||||
private bool loading;
|
||||
|
||||
private int completedTeleports;
|
||||
|
||||
public static WorldLoadingScreenController Instance { get; private set; }
|
||||
|
||||
public bool IsLoading => loading;
|
||||
|
||||
public static event Action LoadingFinished;
|
||||
|
||||
public void Configure(GameObject screenObject, Slider slider)
|
||||
{
|
||||
if (screenObject != null)
|
||||
{
|
||||
loadingScreenObject = screenObject;
|
||||
}
|
||||
if (slider != null)
|
||||
{
|
||||
progressSlider = slider;
|
||||
}
|
||||
if (!loading)
|
||||
{
|
||||
SetScreenVisible(visible: false);
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogWarning("Multiple WorldLoadingScreenController instances found. Using the latest active instance.", this);
|
||||
}
|
||||
Instance = this;
|
||||
SetScreenVisible(visible: false);
|
||||
SetProgress(0f);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SetLoading(isLoading: false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginLoading(float initialProgress = 0f)
|
||||
{
|
||||
completedTeleports = 0;
|
||||
SetLoading(isLoading: true);
|
||||
SetProgress(initialProgress);
|
||||
}
|
||||
|
||||
public void SetLoading(bool isLoading)
|
||||
{
|
||||
if (loading == isLoading)
|
||||
{
|
||||
SetScreenVisible(isLoading);
|
||||
return;
|
||||
}
|
||||
loading = isLoading;
|
||||
SetScreenVisible(isLoading);
|
||||
if (hideSceneWhileLoading)
|
||||
{
|
||||
if (isLoading)
|
||||
{
|
||||
HideScene();
|
||||
}
|
||||
else
|
||||
{
|
||||
RestoreScene();
|
||||
}
|
||||
}
|
||||
if (isLoading)
|
||||
{
|
||||
DisableMovement();
|
||||
}
|
||||
else
|
||||
{
|
||||
RestoreMovement();
|
||||
}
|
||||
if (!isLoading)
|
||||
{
|
||||
completedTeleports = 0;
|
||||
SetProgress(0f);
|
||||
WorldLoadingScreenController.LoadingFinished?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCheckpoint(float progress)
|
||||
{
|
||||
if (loading && (!(progressSlider != null) || !(progress < progressSlider.value)))
|
||||
{
|
||||
SetProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyLocalPlayerTeleported(bool forceComplete = false)
|
||||
{
|
||||
if (loading)
|
||||
{
|
||||
completedTeleports++;
|
||||
if (!forceComplete && completedTeleports < Mathf.Max(1, requiredTeleportsToComplete))
|
||||
{
|
||||
SetCheckpoint(0.97f);
|
||||
}
|
||||
else
|
||||
{
|
||||
CompleteLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteLoading()
|
||||
{
|
||||
if (loading)
|
||||
{
|
||||
SetProgress(1f);
|
||||
SetLoading(isLoading: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetProgress(float progress)
|
||||
{
|
||||
if (loading && hideSceneWhileLoading)
|
||||
{
|
||||
HideScene();
|
||||
}
|
||||
if (!(progressSlider == null))
|
||||
{
|
||||
progressSlider.minValue = 0f;
|
||||
progressSlider.maxValue = 1f;
|
||||
progressSlider.value = Mathf.Clamp01(progress);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetScreenVisible(bool visible)
|
||||
{
|
||||
if (loadingScreenObject != null)
|
||||
{
|
||||
loadingScreenObject.SetActive(visible);
|
||||
}
|
||||
}
|
||||
|
||||
private void HideScene()
|
||||
{
|
||||
Transform loadingRoot = ((loadingScreenObject != null) ? loadingScreenObject.transform : base.transform);
|
||||
Renderer[] array = UnityEngine.Object.FindObjectsByType<Renderer>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (Renderer renderer in array)
|
||||
{
|
||||
if (!(renderer == null) && !rendererStates.ContainsKey(renderer) && !IsInsideLoadingScreen(renderer.transform, loadingRoot))
|
||||
{
|
||||
rendererStates[renderer] = renderer.enabled;
|
||||
renderer.enabled = false;
|
||||
}
|
||||
}
|
||||
Canvas[] array2 = UnityEngine.Object.FindObjectsByType<Canvas>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (Canvas canvas in array2)
|
||||
{
|
||||
if (!(canvas == null) && !canvasStates.ContainsKey(canvas) && !IsInsideLoadingScreen(canvas.transform, loadingRoot))
|
||||
{
|
||||
canvasStates[canvas] = canvas.enabled;
|
||||
canvas.enabled = false;
|
||||
}
|
||||
}
|
||||
Graphic[] array3 = UnityEngine.Object.FindObjectsByType<Graphic>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (Graphic graphic in array3)
|
||||
{
|
||||
if (!(graphic == null) && !graphicStates.ContainsKey(graphic) && !IsInsideLoadingScreen(graphic.transform, loadingRoot))
|
||||
{
|
||||
graphicStates[graphic] = graphic.enabled;
|
||||
graphic.enabled = false;
|
||||
}
|
||||
}
|
||||
Camera[] array4 = UnityEngine.Object.FindObjectsByType<Camera>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (Camera camera in array4)
|
||||
{
|
||||
if (!(camera == null) && !cameraStates.ContainsKey(camera))
|
||||
{
|
||||
cameraStates[camera] = new CameraState
|
||||
{
|
||||
clearFlags = camera.clearFlags,
|
||||
backgroundColor = camera.backgroundColor
|
||||
};
|
||||
camera.clearFlags = CameraClearFlags.Color;
|
||||
camera.backgroundColor = Color.black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreScene()
|
||||
{
|
||||
foreach (KeyValuePair<Renderer, bool> rendererState in rendererStates)
|
||||
{
|
||||
if (rendererState.Key != null)
|
||||
{
|
||||
rendererState.Key.enabled = rendererState.Value;
|
||||
}
|
||||
}
|
||||
foreach (KeyValuePair<Canvas, bool> canvasState in canvasStates)
|
||||
{
|
||||
if (canvasState.Key != null)
|
||||
{
|
||||
canvasState.Key.enabled = canvasState.Value;
|
||||
}
|
||||
}
|
||||
foreach (KeyValuePair<Graphic, bool> graphicState in graphicStates)
|
||||
{
|
||||
if (graphicState.Key != null)
|
||||
{
|
||||
graphicState.Key.enabled = graphicState.Value;
|
||||
}
|
||||
}
|
||||
foreach (KeyValuePair<Camera, CameraState> cameraState in cameraStates)
|
||||
{
|
||||
if (cameraState.Key != null)
|
||||
{
|
||||
cameraState.Key.clearFlags = cameraState.Value.clearFlags;
|
||||
cameraState.Key.backgroundColor = cameraState.Value.backgroundColor;
|
||||
}
|
||||
}
|
||||
rendererStates.Clear();
|
||||
canvasStates.Clear();
|
||||
graphicStates.Clear();
|
||||
cameraStates.Clear();
|
||||
}
|
||||
|
||||
private void DisableMovement()
|
||||
{
|
||||
Behaviour[] array = UnityEngine.Object.FindObjectsByType<Behaviour>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (Behaviour behaviour in array)
|
||||
{
|
||||
if (!(behaviour == null) && !movementBehaviourStates.ContainsKey(behaviour) && IsMovementBehaviour(behaviour))
|
||||
{
|
||||
movementBehaviourStates[behaviour] = behaviour.enabled;
|
||||
behaviour.enabled = false;
|
||||
}
|
||||
}
|
||||
Rigidbody[] array2 = UnityEngine.Object.FindObjectsByType<Rigidbody>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (Rigidbody rigidbody in array2)
|
||||
{
|
||||
if (!(rigidbody == null) && !rigidbodyStates.ContainsKey(rigidbody))
|
||||
{
|
||||
rigidbodyStates[rigidbody] = new RigidbodyState
|
||||
{
|
||||
linearVelocity = rigidbody.linearVelocity,
|
||||
angularVelocity = rigidbody.angularVelocity
|
||||
};
|
||||
rigidbody.linearVelocity = Vector3.zero;
|
||||
rigidbody.angularVelocity = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreMovement()
|
||||
{
|
||||
foreach (KeyValuePair<Behaviour, bool> movementBehaviourState in movementBehaviourStates)
|
||||
{
|
||||
if (movementBehaviourState.Key != null)
|
||||
{
|
||||
movementBehaviourState.Key.enabled = movementBehaviourState.Value;
|
||||
}
|
||||
}
|
||||
foreach (KeyValuePair<Rigidbody, RigidbodyState> rigidbodyState in rigidbodyStates)
|
||||
{
|
||||
if (rigidbodyState.Key != null)
|
||||
{
|
||||
rigidbodyState.Key.linearVelocity = rigidbodyState.Value.linearVelocity;
|
||||
rigidbodyState.Key.angularVelocity = rigidbodyState.Value.angularVelocity;
|
||||
}
|
||||
}
|
||||
movementBehaviourStates.Clear();
|
||||
rigidbodyStates.Clear();
|
||||
}
|
||||
|
||||
private bool IsMovementBehaviour(Behaviour behaviour)
|
||||
{
|
||||
if (behaviour == this)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string text = behaviour.GetType().Name;
|
||||
if (!((behaviour.GetType().FullName ?? string.Empty) == "GorillaLocomotion.Player") && !(text == "DesktopRigController") && !(text == "CreatorFlightController") && !text.Contains("LocomotionProvider") && !text.Contains("MoveProvider") && !text.Contains("TurnProvider") && !text.Contains("JumpProvider") && !text.Contains("TeleportationProvider") && !text.Contains("ClimbProvider") && !text.Contains("ContinuousMove") && !text.Contains("DynamicMove") && !text.Contains("SnapTurn"))
|
||||
{
|
||||
return text.Contains("SmoothTurn");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsInsideLoadingScreen(Transform target, Transform loadingRoot)
|
||||
{
|
||||
if (target != null && loadingRoot != null)
|
||||
{
|
||||
if (!(target == loadingRoot) && !target.IsChildOf(loadingRoot))
|
||||
{
|
||||
return loadingRoot.IsChildOf(target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcf37729cb5d99fabff7ea5e8d1fbccc
|
||||
timeCreated: 1780325055
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
externalObjects: {}
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user