JuanLouTech
JuanLouTech3mo ago

How can I trigger a change on a remotely owned model and get it synced on all clients?

Hi everyone! I need to trigger a change in a remotely owned model (I need to be able to trigger a function on a remote user and get it synced for all clients. I want the players to be able to "push" other players). I tried creating a model with a trigger (classic int hack) to synchronize the action on all clients and adding this model to the player prefab so when the trigger changes a function is called on that prefab instance, but this did not work because the player that is trying the pushing is not the owner of the model in the other player, so the model is not updated.
9 Replies
kapa.ai
kapa.ai3mo ago
Hi @JuanLouTech. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To trigger a change on a remotely owned model and get it synced on all clients, you can use the RealtimeModel and RealtimeComponent features of NormalVR. Firstly, you need to create a RealtimeModel that will hold the data you want to synchronize. In your case, this could be a trigger or a state that represents the "push" action. You can add a change event to this model which will fire when a property is changed locally or remotely. This is a useful signal to update your scene to match the model (source (https://normcore.io/documentation/realtime/synchronizing-custom-data#change-event)). Here is an example of how you can define such a model:
using UnityEngine;
using Normal.Realtime.Serialization;

[RealtimeModel]
public partial class PushActionModel {
[RealtimeProperty(1, true)]
private bool _pushTrigger;
}
using UnityEngine;
using Normal.Realtime.Serialization;

[RealtimeModel]
public partial class PushActionModel {
[RealtimeProperty(1, true)]
private bool _pushTrigger;
}
Next, you need to create a RealtimeComponent subclass that will keep a specific part of your scene in sync with this model. When the game object changes, the component updates the model, and when the model changes, the component updates the game object to match (source (https://normcore.io/documentation/realtime/realtimecomponent)). Here is an example of how you can define such a component:
using UnityEngine;
using Normal.Realtime;

public class PushAction : RealtimeComponent<PushActionModel> {

protected override void OnRealtimeModelReplaced(PushActionModel previousModel, PushActionModel currentModel) {
if (previousModel != null) {
// Unregister from events
previousModel.pushTriggerDidChange -= PushTriggerDidChange;
}

if (currentModel != null) {
// Register for events so we'll know if the push trigger changes later
currentModel.pushTriggerDidChange += PushTriggerDidChange;
}
}

private void PushTriggerDidChange(PushActionModel model, bool value) {
// Update the game object
// This is where you put your code to "push" the player
}
}
using UnityEngine;
using Normal.Realtime;

public class PushAction : RealtimeComponent<PushActionModel> {

protected override void OnRealtimeModelReplaced(PushActionModel previousModel, PushActionModel currentModel) {
if (previousModel != null) {
// Unregister from events
previousModel.pushTriggerDidChange -= PushTriggerDidChange;
}

if (currentModel != null) {
// Register for events so we'll know if the push trigger changes later
currentModel.pushTriggerDidChange += PushTriggerDidChange;
}
}

private void PushTriggerDidChange(PushActionModel model, bool value) {
// Update the game object
// This is where you put your code to "push" the player
}
}
Finally, to trigger a change on a remotely owned model, you can use the RPC (Remote Procedure Call) feature of NormalVR. This allows you to call a method on a remote client and get it synced for all clients (source (https://normcore.io/documentation/guides/recipes/rpc-events#recipe)). Here is an example of how you can add an RPC method to your model:
[RealtimeModel]
public partial class PushActionModel {
[RealtimeProperty(1, true)]
private bool _pushTrigger;

[RealtimeRPC("FirePush")]
private void FirePush() {
_pushTrigger = true;
}
}
[RealtimeModel]
public partial class PushActionModel {
[RealtimeProperty(1, true)]
private bool _pushTrigger;

[RealtimeRPC("FirePush")]
private void FirePush() {
_pushTrigger = true;
}
}
And here is how you can call this method from your component:
public class PushAction : RealtimeComponent<PushActionModel> {
// ...

public void TriggerPush() {
model.FirePush();
}
// ...
}
public class PushAction : RealtimeComponent<PushActionModel> {
// ...

public void TriggerPush() {
model.FirePush();
}
// ...
}
Please note that this is a simplified example and you might need to adjust it to fit your specific use case. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@941753147938984038> kapa.ai is still learning and improving, please let us know how it did by reacting below
JuanLouTech
JuanLouTech3mo ago
@Normcore Assistant , are RPC messages usable on WebGL? I am working on a Web application.
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- No, RPC messages are not currently usable on WebGL. According to the documentation (https://normcore.io/documentation/platforms/webgl#sendrpcmessage-does-not-work), the SendRPCMessage feature is intentionally disabled in the WebGL alpha. The plan is to deprecate this API in Normcore 3 and replace it with a more feature-rich version. If you need this functionality in your project, you are advised to contact the NormalVR team for a version with the legacy RPC API enabled. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@941753147938984038> kapa.ai is still learning and improving, please let us know how it did by reacting below
Mechabit
Mechabit3mo ago
you do the same thing but on a realtime set that's unowned you add the target player in the model so they know to trigger this effect on themselves anyone can add models to sets that are unowned this is the typical way to send messages to players like damage every player listens to the model added event @Normcore Assistant how do you use a realtime set to send a damage messages to another player
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To send damage messages to another player, you can use a RealtimeSet to store and manage these messages. RealtimeSet is an unordered collection of RealtimeModels and can be used to represent instantiated prefabs. Models can be added and removed at runtime by any clients, simultaneously, and without conflict (source (https://normcore.io/documentation/room/collections#non-transactional-collections)). First, you need to create a RealtimeModel to represent a damage message. This model could include properties like the sender ID, the receiver ID, and the amount of damage:
[RealtimeModel]
public partial class DamageMessageModel {
[RealtimeProperty(1, true)]
private int _senderID;
[RealtimeProperty(2, true)]
private int _receiverID;
[RealtimeProperty(3, true)]
private float _damageAmount;
}
[RealtimeModel]
public partial class DamageMessageModel {
[RealtimeProperty(1, true)]
private int _senderID;
[RealtimeProperty(2, true)]
private int _receiverID;
[RealtimeProperty(3, true)]
private float _damageAmount;
}
Then, you can create a RealtimeSet to store instances of this model:
RealtimeSet<DamageMessageModel> damageMessages = new RealtimeSet<DamageMessageModel>();
RealtimeSet<DamageMessageModel> damageMessages = new RealtimeSet<DamageMessageModel>();
To send a damage message, you can create a new DamageMessageModel and add it to the RealtimeSet:
DamageMessageModel damageMessage = new DamageMessageModel();
damageMessage.senderID = myID;
damageMessage.receiverID = otherPlayerID;
damageMessage.damageAmount = damage;
damageMessages.Add(damageMessage);
DamageMessageModel damageMessage = new DamageMessageModel();
damageMessage.senderID = myID;
damageMessage.receiverID = otherPlayerID;
damageMessage.damageAmount = damage;
damageMessages.Add(damageMessage);
Other players can then listen for changes to the RealtimeSet and react accordingly when a new damage message is added:
damageMessages.DidAdd += (set, message) => {
if (message.receiverID == myID) {
// Apply damage
}
};
damageMessages.DidAdd += (set, message) => {
if (message.receiverID == myID) {
// Apply damage
}
};
Please note that this is a simplified example and you might need to adjust it to fit your specific use case. Also, remember that RealtimeSet uses a last-client-wins approach to merge changes, so if you need to control how a merge conflict is handled, you might want to use a transactional collection instead. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@162940911964258305> kapa.ai is still learning and improving, please let us know how it did by reacting below
JuanLouTech
JuanLouTech2mo ago
Thanks! I did not know about this. The way I was handling events and triggers was with the typical int increment in a custom model. This is way better for interactions between players.