kapa.ai
NNormal
•Created by Niv on 4/7/2025 in #🤝ask-a-question
Is there a way to setup the server/client with a bigger timeout so it will not disconnect easily
11 replies
NNormal
•Created by Niv on 4/7/2025 in #🤝ask-a-question
Is there a way to setup the server/client with a bigger timeout so it will not disconnect easily
----------------------
You're absolutely right. When a breakpoint is hit, code execution is paused, so there's no way to periodically call
BumpAutoDisconnectTime()
during that pause.
For development scenarios where you're using breakpoints, there are a few alternative approaches you might consider:
1. You could temporarily use an offline mode for development and debugging. Normcore provides an OfflineClient
that doesn't require a connection to a server.
2. For debugging specific functionality, you might set up your debugging environment to run with a local room server if possible, which would give you more control over the timeout settings.
3. Another approach is to structure your debugging sessions to be aware of the timeout limitation - for example, by setting breakpoints strategically and resuming execution before the timeout occurs.
Unfortunately, the knowledge sources don't provide a direct solution for extending the server timeout specifically during breakpoint pauses. The fundamental issue is that when execution is paused at a breakpoint, no code can run to keep the connection alive.
If this is a significant issue for your development workflow, you might want to reach out to the Normcore community on their Discord for additional suggestions or workarounds that other developers might be using.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------11 replies
NNormal
•Created by Niv on 4/7/2025 in #🤝ask-a-question
Is there a way to setup the server/client with a bigger timeout so it will not disconnect easily
Let me check the knowledge base for you again. One moment! :books:
11 replies
NNormal
•Created by Niv on 4/7/2025 in #🤝ask-a-question
Is there a way to setup the server/client with a bigger timeout so it will not disconnect easily
11 replies
NNormal
•Created by Niv on 4/7/2025 in #🤝ask-a-question
Is there a way to setup the server/client with a bigger timeout so it will not disconnect easily
----------------------
Yes, you can prevent automatic disconnections during development when using breakpoints or pausing the editor in Normcore. The
BumpAutoDisconnectTime()
method is available for this purpose.
When developing with Normcore, the server pings connected clients every five seconds to check their connection status. If a client doesn't respond (which happens when you're paused in a breakpoint), the server will disconnect them after about five seconds as mentioned in the Architecture > Common Questions documentation.
To prevent this during development, you can call the BumpAutoDisconnectTime()
method on the client. This method is available on the native client implementations as shown in the Reference > Classes > IClient, Client, and OfflineClient documentation.
You would need to call this method periodically to keep the connection alive during debugging sessions. You could set up a system that calls this method at regular intervals during development builds to prevent timeouts when you're paused in the editor.
Note that this is specifically for development purposes, and in production, the default timeout behavior helps ensure that crashed clients are properly cleaned up from the room.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------11 replies
NNormal
•Created by Niv on 4/7/2025 in #🤝ask-a-question
Is there a way to setup the server/client with a bigger timeout so it will not disconnect easily
Hi @Niv. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
11 replies
NNormal
•Created by beeeen on 4/4/2025 in #🤝ask-a-question
How can I keep my Realtime prefabs out of the Normal/Resources folder?
14 replies
NNormal
•Created by beeeen on 4/4/2025 in #🤝ask-a-question
How can I keep my Realtime prefabs out of the Normal/Resources folder?
2. Add this component to the same GameObject as your Realtime component.
3. Set up your Addressables system and add your Realtime prefabs to it.
4. When instantiating, you'll use the asset key as the prefab name:
This approach allows you to organize your prefabs however you want in your project structure while still making them available to Normcore at runtime.
Using Addressables
By implementing this system, you can keep your prefabs organized in a way that makes sense for your project structure rather than being forced to use Resources folders.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
14 replies
NNormal
•Created by beeeen on 4/4/2025 in #🤝ask-a-question
How can I keep my Realtime prefabs out of the Normal/Resources folder?
Debug.LogWarning($"CustomPrefabLoadDelegate: Asked to load a prefab that doesn't exist in our list of preloaded assets. Will load synchronously, but this may be slow if the asset isn't already loaded.");
// Start loading the asset
asset = Addressables.LoadAssetAsync<GameObject>(key);
// Store the load operation for future calls
_assets.Add(key, asset);
}
// Wait for the asset to load (anything preloaded will return instantly)
return asset.WaitForCompletion();
}
} ```
// Start loading the asset
asset = Addressables.LoadAssetAsync<GameObject>(key);
// Store the load operation for future calls
_assets.Add(key, asset);
}
// Wait for the asset to load (anything preloaded will return instantly)
return asset.WaitForCompletion();
}
} ```
14 replies
NNormal
•Created by beeeen on 4/4/2025 in #🤝ask-a-question
How can I keep my Realtime prefabs out of the Normal/Resources folder?
```csharp
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Normal.Realtime;
public class CustomPrefabLoadDelegate : MonoBehaviour, IRealtimePrefabLoadDelegate {
[SerializeField]
private List<AssetReference> _realtimePrefabsToPreload = new List<AssetReference>();
private Dictionary<string, AsyncOperationHandle<GameObject>> _assets;
private void Start() {
// Preload all assets
_assets = new Dictionary<string, AsyncOperationHandle<GameObject>>();
foreach (AssetReference assetToLoad in _realtimePrefabsToPreload) {
if (assetToLoad.RuntimeKeyIsValid() == false)
continue;
string key = assetToLoad.RuntimeKey as string;
if (key == null)
continue;
_assets.Add(key, Addressables.LoadAssetAsync<GameObject>(key));
}
}
private void OnDestroy() {
// Unload all assets
// Note: Make sure all of your realtime prefabs are destroyed before this script is destroyed otherwise it may unload assets that are in use by realtime prefabs in the scene.
foreach (var assetHandle in _assets.Values) {
Addressables.Release(assetHandle);
}
}
public GameObject LoadRealtimePrefab(RealtimePrefabMetadata prefabMetadata) {
string key = prefabMetadata.prefabName;
AsyncOperationHandle<GameObject> asset = default;
// Check if we have already preloaded this asset.
if (_assets.TryGetValue(key, out asset) == false) {
// Not found, log a warning that the asset will need to be loaded into memory.
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Normal.Realtime;
public class CustomPrefabLoadDelegate : MonoBehaviour, IRealtimePrefabLoadDelegate {
[SerializeField]
private List<AssetReference> _realtimePrefabsToPreload = new List<AssetReference>();
private Dictionary<string, AsyncOperationHandle<GameObject>> _assets;
private void Start() {
// Preload all assets
_assets = new Dictionary<string, AsyncOperationHandle<GameObject>>();
foreach (AssetReference assetToLoad in _realtimePrefabsToPreload) {
if (assetToLoad.RuntimeKeyIsValid() == false)
continue;
string key = assetToLoad.RuntimeKey as string;
if (key == null)
continue;
_assets.Add(key, Addressables.LoadAssetAsync<GameObject>(key));
}
}
private void OnDestroy() {
// Unload all assets
// Note: Make sure all of your realtime prefabs are destroyed before this script is destroyed otherwise it may unload assets that are in use by realtime prefabs in the scene.
foreach (var assetHandle in _assets.Values) {
Addressables.Release(assetHandle);
}
}
public GameObject LoadRealtimePrefab(RealtimePrefabMetadata prefabMetadata) {
string key = prefabMetadata.prefabName;
AsyncOperationHandle<GameObject> asset = default;
// Check if we have already preloaded this asset.
if (_assets.TryGetValue(key, out asset) == false) {
// Not found, log a warning that the asset will need to be loaded into memory.
14 replies
NNormal
•Created by beeeen on 4/4/2025 in #🤝ask-a-question
How can I keep my Realtime prefabs out of the Normal/Resources folder?
----------------------
You don't need to keep your Realtime prefabs specifically in a "Normal/Resources" folder. The requirement is simply that they need to be in any "Resources" folder within your project so they can be loaded at runtime.
However, if you want to avoid using Resources folders altogether (which is recommended for larger projects due to performance issues), Normcore provides a way to use the Addressables system instead.
Normcore includes two interfaces that can hook into the prefab loading and instantiation pipeline:
-
IRealtimePrefabLoadDelegate
- IRealtimePrefabInstantiateDelegate
You can implement a custom prefab load delegate to use Addressables instead of Resources. Here's how you can set it up:
1. Create a script that implements IRealtimePrefabLoadDelegate
:14 replies
NNormal
•Created by beeeen on 4/4/2025 in #🤝ask-a-question
How can I keep my Realtime prefabs out of the Normal/Resources folder?
Hi @beeeen. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
14 replies
NNormal
•Created by jos_valentin on 4/3/2025 in #🤝ask-a-question
Clients and spawn points
5. Prevent Ownership Takeover: Use the
preventOwnershipTakeover
flag to ensure only the designated client can manage spawn points:
"When this flag is true on any model, the server will deny any client's attempt to change the ownership of an object in that model unless it has no owner." Prevent Ownership TakeoverThis approach leverages Normcore's built-in synchronization and ownership systems to handle the race condition you're concerned about, without resorting to RPCs which can lead to synchronization issues. For player spawning, you could use the RealtimeAvatarManager which is designed to handle avatar instantiation, and customize it to use your spawn point system. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
9 replies
NNormal
•Created by jos_valentin on 4/3/2025 in #🤝ask-a-question
Clients and spawn points
----------------------
Your question about managing spawn points for multiple clients in NormalVR is a common multiplayer challenge. Based on the knowledge sources, I can suggest a good approach for this scenario.
Managing Spawn Points with Ownership
Instead of using RPCs (which Normcore generally discourages), a better approach would be to use Normcore's ownership system combined with a synchronized collection to manage spawn points.
Why Not Use RPCs?
Normcore explicitly warns against using RPC messages:
"Before we dive into this recipe, I'd like to warn you against using RPC messages... We believe this is a bad pattern. It puts the burden on you, the developer, to ensure that all state that is changed in response to an RPC message remains in sync across all clients." RPC EventsRecommended Approach 1. Create a Spawn Manager Component: Make a custom RealtimeComponent that manages spawn points. 2. Use a StringKeyDictionary or RealtimeDictionary: These collections are designed to handle concurrent modifications safely:
"Transactional collections do not reflect a change to the collection until the server has confirmed the change. They also reject your change if another client has modified the same key before your change is received by the server." Collections3. Ownership Control: Have the first client take ownership of the spawn manager: 4. Spawn Point Assignment: When a new player joins, they can request a spawn point from the manager, which will assign one and mark it as used in the synchronized collection.
9 replies
NNormal
•Created by jos_valentin on 4/3/2025 in #🤝ask-a-question
Clients and spawn points
Hi @jos_valentin. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
9 replies
NNormal
•Created by Sachin Pandey on 4/3/2025 in #🤝ask-a-question
Whiteboard for multiplayer using normcore and meta avatar
6 replies
NNormal
•Created by Sachin Pandey on 4/3/2025 in #🤝ask-a-question
Whiteboard for multiplayer using normcore and meta avatar
For a complete implementation example, you can download the finished project from Creating a Multiplayer Drawing App.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
6 replies
NNormal
•Created by Sachin Pandey on 4/3/2025 in #🤝ask-a-question
Whiteboard for multiplayer using normcore and meta avatar
// Turn off the last ribbon point if this brush stroke is finalized
_mesh.skipLastRibbonPoint = model.brushStrokeFinalized;
// Let us know when a new ribbon point is added to the mesh
currentModel.ribbonPoints.modelAdded += RibbonPointAdded; } } csharp private void AddRibbonPointIfNeeded() { // Only add ribbon points if this brush stroke is being drawn by the local client. if (!realtimeView.isOwnedLocallySelf) return; // If the brush stroke is finalized, stop trying to add points to it. if (model.brushStrokeFinalized) return; if (Vector3.Distance(_ribbonEndPosition, _previousRibbonPointPosition) >= 0.01f || Quaternion.Angle(_ribbonEndRotation, _previousRibbonPointRotation) >= 10.0f) { // Add ribbon point model to ribbon points array. This will fire the RibbonPointAdded event to update the mesh. AddRibbonPoint(_ribbonEndPosition, _ribbonEndRotation); // Store the ribbon point position & rotation for the next time we do this calculation _previousRibbonPointPosition = _ribbonEndPosition; _previousRibbonPointRotation = _ribbonEndRotation; } }
_mesh.skipLastRibbonPoint = model.brushStrokeFinalized;
// Let us know when a new ribbon point is added to the mesh
currentModel.ribbonPoints.modelAdded += RibbonPointAdded; } } csharp private void AddRibbonPointIfNeeded() { // Only add ribbon points if this brush stroke is being drawn by the local client. if (!realtimeView.isOwnedLocallySelf) return; // If the brush stroke is finalized, stop trying to add points to it. if (model.brushStrokeFinalized) return; if (Vector3.Distance(_ribbonEndPosition, _previousRibbonPointPosition) >= 0.01f || Quaternion.Angle(_ribbonEndRotation, _previousRibbonPointRotation) >= 10.0f) { // Add ribbon point model to ribbon points array. This will fire the RibbonPointAdded event to update the mesh. AddRibbonPoint(_ribbonEndPosition, _ribbonEndRotation); // Store the ribbon point position & rotation for the next time we do this calculation _previousRibbonPointPosition = _ribbonEndPosition; _previousRibbonPointRotation = _ribbonEndRotation; } }
``
You might need to decrease the distance threshold (0.01f) to add points more frequently.
4. **Ensure proper event handling**: Make sure you're properly handling the
RibbonPointAdded` event to update the mesh on all clients:6 replies
NNormal
•Created by Sachin Pandey on 4/3/2025 in #🤝ask-a-question
Whiteboard for multiplayer using normcore and meta avatar
----------------------
It sounds like you're experiencing an issue with your multiplayer whiteboard implementation using Normcore, where brush strokes appear complete to the owner but appear dotted or incomplete to other players.
This issue is likely related to how the brush stroke data is being synchronized across clients. Based on the knowledge sources, here are some suggestions to fix this problem:
1. Check your ribbon point synchronization: The multiplayer drawing app example in Normcore uses a
_mesh.ClearRibbon();
if (previousModel != null) { // Unregister from events
previousModel.ribbonPoints.modelAdded -= RibbonPointAdded; }
if (currentModel != null) { // Replace ribbon mesh
foreach (RibbonPointModel ribbonPoint in currentModel.ribbonPoints) _mesh.InsertRibbonPoint(ribbonPoint.position, ribbonPoint.rotation); // Update last ribbon point to match brush tip position & rotation
_ribbonEndPosition = model.brushTipPosition; _ribbonEndRotation = model.brushTipRotation; _mesh.UpdateLastRibbonPoint(model.brushTipPosition, model.brushTipRotation);
RealtimeArray<RibbonPointModel>
to synchronize brush strokes. Make sure you're properly adding points to this array and that the modelAdded
event is being handled correctly on all clients. Creating a Multiplayer Drawing App shows how this should be implemented.
2. Implement proper model replacement handling: When a player joins late, they need to properly render existing brush strokes. Make sure you have code similar to this in your OnRealtimeModelReplaced
method:
```csharp
protected override void OnRealtimeModelReplaced(BrushStrokeModel previousModel, BrushStrokeModel currentModel) {
// Clear Mesh_mesh.ClearRibbon();
if (previousModel != null) { // Unregister from events
previousModel.ribbonPoints.modelAdded -= RibbonPointAdded; }
if (currentModel != null) { // Replace ribbon mesh
foreach (RibbonPointModel ribbonPoint in currentModel.ribbonPoints) _mesh.InsertRibbonPoint(ribbonPoint.position, ribbonPoint.rotation); // Update last ribbon point to match brush tip position & rotation
_ribbonEndPosition = model.brushTipPosition; _ribbonEndRotation = model.brushTipRotation; _mesh.UpdateLastRibbonPoint(model.brushTipPosition, model.brushTipRotation);
6 replies