Funkey
Funkey2mo ago

How to update a element in a realtimeArray for all clients?

I currently have a realtimeArray of a Vect2 within another Sync script. On a fresh model I loop through the number of players I want the array to account for and I Add a new Vect2 Model to it then subscribe the index to the DidChange events I want. The onChange event does fire locally but it doesn't sync the data of the array to other clients. Should I add the realtimeArray of Vect2 to the model of the Sync script that holds it? I know that I'm subscribing to the event and that the DidChange event fires locally but it never syncs the array to other players. How do I ensure that the synced data goes to every client?
11 Replies
kapa.ai
kapa.ai2mo ago
Hi @Funkey. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The RealtimeArray in Normcore is a special model type that holds a sequential list of models that can be modified at runtime. When you modify the array, it sends the minimal amount of information necessary for other clients to replicate the change. The whole collection is not sent every time. This means that changes to the array should be automatically synced to all clients. However, there are a few things you need to ensure for the syncing to work correctly: 1. The RealtimeArray should be part of a RealtimeModel. If it's not part of a RealtimeModel, it won't be synced across clients. 2. Updates to RealtimeModel objects appear to take effect immediately for the local client, and change events also fire immediately for the local client. However, the server might reject an update due to either ownership or Normcore Private plug-ins. When this happens, the server sends back the rejected update or a modified version of the state in the update. The datastore then rolls the data back or replaces it with the version transformed by the server. Change events then fire again to reflect the new values. 3. Normcore serializes all updates once per network frame (~20hz) and groups them into the same packet. When model updates are applied, they’re applied one at a time, and a change event fires for that value. However, it’s possible the other values from that packet hadn’t yet been applied. In order to get an event that fires once all updates have been applied to a model, use RealtimeCallback. 4. If you're adding elements to the RealtimeArray, you should use the Add method. This appends a RealtimeModel to the end of the array. Add operations take effect instantly. If the server rejects a change (due to ownership) the model will be removed. 5. If you're trying to update an element at a specific index in the RealtimeArray, you should retrieve the model at that index using the Item property, and then modify the model. The changes to the model should then be automatically synced to all clients. Here's an example of how you can add a model to a RealtimeArray and listen for changes:
void Add(TValue value) {
// Appends a RealtimeModel to the end of the array.
// value: The model to add. This value cannot be null.
}

IEnumerator<TValue> GetEnumerator() {
// Retrieve a model given a specific index.
// index: The index of the model to retrieve
}

void _ReadArrayUpdate(ReadStream stream, StreamContext context, uint propertyID) {
// Internal. Do not use.
// This will be removed in the next major version.
}

uint GetCollectionTypeID() {
// Get the type ID of the collection.
}
void Add(TValue value) {
// Appends a RealtimeModel to the end of the array.
// value: The model to add. This value cannot be null.
}

IEnumerator<TValue> GetEnumerator() {
// Retrieve a model given a specific index.
// index: The index of the model to retrieve
}

void _ReadArrayUpdate(ReadStream stream, StreamContext context, uint propertyID) {
// Internal. Do not use.
// This will be removed in the next major version.
}

uint GetCollectionTypeID() {
// Get the type ID of the collection.
}
Sources: - RealtimeArray Documentation (https://normcore.io/documentation/reference/classes/Normal.Realtime.Serialization.RealtimeArray%601) - Instant Updates Documentation (https://normcore.io/documentation/room/datastore#instant-updates) - Common Questions Documentation (https://normcore.io/documentation/room/common-questions#sometimes-when-i-log-the-values-on-my-model-from-inside-of-valuedidchange-not-all-the-values-that-i-set-on-another-client-update-when-are-updates-sent-out-and-how-are-they-applied) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@304126447302344705> kapa.ai is still learning and improving, please let us know how it did by reacting below
Funkey
Funkey2mo ago
@Normcore Assistant
if (currentModel != null) {
// a model with no current data
if (currentModel.isFreshModel) {

// create players array
if (_players.Count <= 0) {
for (int i = 0; i < MAX_PLAYERS; i++) {
currentModel.players.Add(new PlayerModel());
currentModel.players[i].readyDidChange += ReadyDidChange;
}
}
}
if (currentModel != null) {
// a model with no current data
if (currentModel.isFreshModel) {

// create players array
if (_players.Count <= 0) {
for (int i = 0; i < MAX_PLAYERS; i++) {
currentModel.players.Add(new PlayerModel());
currentModel.players[i].readyDidChange += ReadyDidChange;
}
}
}
c# Would something like this work then? The model now has the realtimeArray called players and I'm subscribing to it's event on a fresh model like this.
private void ReadyDidChange(PlayerModel model, int value){
int index = 0;
int i;
for(i=0; i< 4; i++){
if(_players[i].Equals(model)){
Debug.Log($"Player {i+1} changed ready status to {value}");
index = i;
}
}
UpdateArrayReady(value, index);
}

private void UpdateArrayReady(int value, int index) {
// _players[0].ready = value;
_players[index].ready = value;
Debug.Log($"New array readies: [{_players[0].ready}, {_players[1].ready}, {_players[2].ready}, {_players[3].ready}]");
}
private void ReadyDidChange(PlayerModel model, int value){
int index = 0;
int i;
for(i=0; i< 4; i++){
if(_players[i].Equals(model)){
Debug.Log($"Player {i+1} changed ready status to {value}");
index = i;
}
}
UpdateArrayReady(value, index);
}

private void UpdateArrayReady(int value, int index) {
// _players[0].ready = value;
_players[index].ready = value;
Debug.Log($"New array readies: [{_players[0].ready}, {_players[1].ready}, {_players[2].ready}, {_players[3].ready}]");
}
c# Then above is how I'm handling the change.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, your approach seems correct based on the information provided in the knowledge sources. You are creating a RealtimeArray of PlayerModel in your RealtimeModel and adding a new PlayerModel for each player. You are also correctly subscribing to the readyDidChange event for each PlayerModel in the array. When the readyDidChange event is fired, you are finding the index of the player whose ready status changed, and then updating the ready status of the player at that index in the _players array. Here's a simplified version of your code:
if (currentModel != null) {
// a model with no current data
if (currentModel.isFreshModel) {
// create players array
if (_players.Count <= 0) {
for (int i = 0; i < MAX_PLAYERS; i++) {
currentModel.players.Add(new PlayerModel());
currentModel.players[i].readyDidChange += ReadyDidChange;
}
}
}
}

private void ReadyDidChange(PlayerModel model, int value){
int index = _players.IndexOf(model);
if (index != -1) {
Debug.Log($"Player {index+1} changed ready status to {value}");
UpdateArrayReady(value, index);
}
}

private void UpdateArrayReady(int value, int index) {
_players[index].ready = value;
Debug.Log($"New array readies: [{string.Join(", ", _players.Select(p => p.ready))}]");
}
if (currentModel != null) {
// a model with no current data
if (currentModel.isFreshModel) {
// create players array
if (_players.Count <= 0) {
for (int i = 0; i < MAX_PLAYERS; i++) {
currentModel.players.Add(new PlayerModel());
currentModel.players[i].readyDidChange += ReadyDidChange;
}
}
}
}

private void ReadyDidChange(PlayerModel model, int value){
int index = _players.IndexOf(model);
if (index != -1) {
Debug.Log($"Player {index+1} changed ready status to {value}");
UpdateArrayReady(value, index);
}
}

private void UpdateArrayReady(int value, int index) {
_players[index].ready = value;
Debug.Log($"New array readies: [{string.Join(", ", _players.Select(p => p.ready))}]");
}
This code should work as expected, as long as the _players array is kept in sync with the players RealtimeArray in the RealtimeModel. Sources: - RealtimeArray Documentation (https://normcore.io/documentation/reference/classes/Normal.Realtime.Serialization.RealtimeArray%601) - RealtimeModel Documentation (https://normcore.io/documentation/room/realtimemodel#change-event) - RealtimeComponent Documentation (https://normcore.io/documentation/realtime/synchronizing-custom-data#creating-a-realtimecomponent-subclass) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@304126447302344705> kapa.ai is still learning and improving, please let us know how it did by reacting below
Funkey
Funkey2mo ago
@Normcore Assistant The code I provided in the last example doesn't seem to work. I get an error when I attempt to index into the array.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I'm sorry to hear that you're encountering an error. Based on the information provided, it's difficult to pinpoint the exact issue without more details about the error message or the context in which the error occurs. However, here are a few general suggestions that might help: 1. Check the size of the array: Ensure that the _players array has been properly initialized and that it contains the expected number of elements. If you're trying to access an index that is outside the bounds of the array, you will get an IndexOutOfRangeException. 2. Check the index value: Make sure that the index value you're using to access the array is valid. If the index value is negative or greater than or equal to the length of the array, you will get an IndexOutOfRangeException. 3. Check the array initialization: Ensure that the RealtimeArray of PlayerModel is properly initialized in your RealtimeModel. If the array is null, trying to access it will result in a NullReferenceException. 4. Check the event subscription: Make sure that the readyDidChange event is properly subscribed to for each PlayerModel in the array. If the event is not firing as expected, the ReadyDidChange method will not be called, and the ready status of the players will not be updated. If you're still encountering issues, I would recommend checking the Normcore Documentation (https://normcore.io/documentation) for more information, or joining the Normcore Discord (https://normcore.io/discord) to ask for help from the community. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@304126447302344705> kapa.ai is still learning and improving, please let us know how it did by reacting below
Funkey
Funkey2mo ago
@Normcore Assistant So it's acting like the data is syncing but the onChange doens't fire across all clients. It seems that the data is being synced based off behavior but I'm looping through the synced array to verify values onChange and logging them. The debugLog only fires on the client that changes the value and it doesn't account for everyone else's changed values.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Based on the information provided, it seems like the readyDidChange event is only firing locally and not on all clients. This could be due to a few reasons: 1. Event Subscription: Ensure that the readyDidChange event is subscribed to on all clients. If the event subscription only happens on one client, the event will only fire on that client. 2. Data Synchronization: Normcore serializes all updates once per network frame (~20hz) and groups them into the same packet. When model updates are applied, they’re applied one at a time, and a change event fires for that value. However, it’s possible the other values from that packet hadn’t yet been applied. 3. Server Rejection: Updates to RealtimeModel objects appear to take effect immediately for the local client. However, the server might reject an update due to either ownership or Normcore Private plug-ins. When this happens, the server sends back the rejected update or a modified version of the state in the update. The datastore then rolls the data back or replaces it with the version transformed by the server. Change events then fire again to reflect the new values. If you're still encountering issues, I would recommend checking the Normcore Documentation (https://normcore.io/documentation) for more information, or joining the Normcore Discord (https://normcore.io/discord) to ask for help from the community. Sources: - Common Questions Documentation (https://normcore.io/documentation/room/common-questions#sometimes-when-i-log-the-values-on-my-model-from-inside-of-valuedidchange-not-all-the-values-that-i-set-on-another-client-update-when-are-updates-sent-out-and-how-are-they-applied) - Instant Updates Documentation (https://normcore.io/documentation/room/datastore#instant-updates) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@304126447302344705> kapa.ai is still learning and improving, please let us know how it did by reacting below