fix prop diff case of no emit to sender

This commit is contained in:
Rolands 2026-01-07 10:23:12 +02:00
parent c448d876b5
commit 6c7dc612a1
4 changed files with 27 additions and 8 deletions

View file

@ -240,19 +240,20 @@ export class SocioClient extends LogHandler {
let prop_val: PropValue | diff_lib.rdiffResult[] | undefined;
if (prop) {
if (typeof prop.subs[(data as C_PROP_UPD_data).id] === 'function') {
// log('prop upd has:', (data as C_PROP_UPD_data).hasOwnProperty('prop_val'), (data as C_PROP_UPD_data).hasOwnProperty('prop_val_diff'));
// get the new factual value value of the prop either from the server or from applying the diff from server
if ((data as C_PROP_UPD_data).hasOwnProperty('prop_val')) { //take factual
prop_val = (data as C_PROP_UPD_data).prop_val;
}
else if ((data as C_PROP_UPD_data).hasOwnProperty('prop_val_diff')) { //apply diff
prop_val = diff_lib.applyDiff(prop.val, (data as C_PROP_UPD_data).prop_val_diff!);
prop_val = diff_lib.applyDiff(prop.val, (data as C_PROP_UPD_data).prop_val_diff as diff_lib.rdiffResult[]);
}
else throw new E('Prop upd data didnt have either factual val or diff val to use. [#prop-upd-no-val]', { kind, data });
prop.val = prop_val; //set the new val
// afterwards call the client subscription with the new val and the received diff
//@ts-expect-error
prop.subs[(data as C_PROP_UPD_data).id as id](prop_val, (data as C_PROP_UPD_data)?.prop_val_diff || undefined);
prop.subs[(data as C_PROP_UPD_data).id as id](prop.val, (data as C_PROP_UPD_data)?.prop_val_diff as diff_lib.rdiffResult[] || undefined);
} else {
throw new E('Prop UPD called, but subscribed prop is missing a registered callback for this data ID. [#prop-subs-id-not-found]', { data, callback: prop.subs[(data as C_PROP_UPD_data).id] });
}
@ -546,14 +547,30 @@ export class SocioClient extends LogHandler {
}
SetProp(prop_name: PropKey, new_val: PropValue, prop_upd_as_diff?: boolean) {
async SetProp(prop_name: PropKey, new_val: PropValue, prop_upd_as_diff?: boolean) {
try {
//set up a promise which resolve function is in the queries data structure, such that in the message handler it can be called, therefor the promise resolved, therefor awaited and return from this function
const { id, prom } = this.CreateQueryPromise();
this.Send(ServerMessageKind.PROP_SET, { id, prop: prop_name, prop_val: new_val, prop_upd_as_diff } as S_PROP_SET_data);
this.#UpdateQueryPromisePayloadSize(id);
return prom as Promise<data_base & data_result_block>;
//update local cache too, if the server accepted the change. This is important, because the server might have validation logic that rejects the set,
// but also that prop might have set to not emit to sender, so this client receiving diffs wouldnt have its own val updates.
const res = await (prom as Promise<data_base & data_result_block>); //wait for the server to ack the set
if(res.result.success === 1){ //if success, then update local cache
const prop = this.#props.get(prop_name);
if(prop){
prop.val = new_val;
// Could be a deeply proxied object, of which any layer could be marked as structured clone blocking,
// which could cause object copy problems in other parts of the code later,
// but it does get serialized over the network to others, which clones it,
// but still local value references the passed object. Kinda weird,
// basically some operations with the local val might error, but not the ones coming in from other clients
// but also when a new val is received from the server, it should overwrite this reference anyway.
}
}
return res;
} catch (e: err) { this.#HandleClientError(e); return null; }
}
GetProp(prop_name: PropKey, local: boolean = false) {

View file

@ -895,6 +895,7 @@ export class SocioServer extends LogHandler {
const new_assigned_prop_val = this.GetPropVal(key); //should be GetPropVal, bcs i cant know how the assigner changed the val. But since it runs once per update, then i can cache this call here right after the assigner.
const prop_val_diff = diff_lib.getDiff(old_prop_val, new_assigned_prop_val);
if (prop_val_diff.length === 0 && force === false) return 2; //dont do anything further, if the prop val didnt actually change. This is efficient and removes long feedback loops for global props across many users. Return 0 to indicate no update was sent.
// log(`Prop [${key}] updated.`, { old_prop_val, new_assigned_prop_val, prop_val_diff });
for (const [client_id, args] of prop.updates.entries()) {
if (args?.rate_limiter && args.rate_limiter?.CheckLimit()) continue; //ratelimit check for this client
@ -906,7 +907,8 @@ export class SocioServer extends LogHandler {
const upd_data = { id: args.id, prop: key };
//overload the global Socio Server flag with a per prop flag
if (prop?.send_as_diff && typeof prop?.send_as_diff == 'boolean') send_as_diff = prop.send_as_diff;
if (prop?.send_as_diff === true) send_as_diff = true;
// log('---Prop update send_as_diff:', send_as_diff, prop?.send_as_diff, this.#prop_upd_diff);
//construct either concrete value or diff of it.
if (send_as_diff)

View file

@ -1,12 +1,12 @@
{
"name": "socio",
"version": "1.15.6",
"version": "1.15.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "socio",
"version": "1.15.6",
"version": "1.15.7",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.2",

View file

@ -1,6 +1,6 @@
{
"name": "socio",
"version": "1.15.6",
"version": "1.15.7",
"description": "A WebSocket Real-Time Communication (RTC) API framework.",
"main": "./dist/core.js",
"type": "module",