Skip to content

Commit

Permalink
Merge pull request #221 from movesthatmatter/state-transformer-master…
Browse files Browse the repository at this point in the history
…-actions

State Transformer + Master Actions
  • Loading branch information
GabrielCTroia authored Sep 26, 2024
2 parents f745354 + ca02917 commit 0a528f1
Show file tree
Hide file tree
Showing 43 changed files with 1,403 additions and 720 deletions.
1 change: 1 addition & 0 deletions apps/movex-docs/pages/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [ ] Improve the Connection Events
- [ ] Master Actions. See the [Pull Request](https://github.com/movesthatmatter/movex/pull/216)
- [ ] State Transformers. See the [Pull Request](https://github.com/movesthatmatter/movex/pull/218)
- [ ] State Transformers applied locally. See the [Pull Request](https://github.com/movesthatmatter/movex/pull/221)

### Breaking

Expand Down
2 changes: 1 addition & 1 deletion libs/movex-core-util/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "movex-core-util",
"version": "0.1.6-45",
"version": "0.1.6-58",
"description": "Movex Core Util is the library of utilities for Movex",
"license": "MIT",
"author": {
Expand Down
2 changes: 1 addition & 1 deletion libs/movex-core-util/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export * from './lib/reducer';
export * from './lib/core-types';
export * from './lib/public-types';
export * from './lib/checkedState';
export * from './lib/masterQueries';
export * from './lib/masterContext';
37 changes: 23 additions & 14 deletions libs/movex-core-util/src/lib/EventEmitter/IOEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import type {
MovexClient,
ResourceIdentifier,
SanitizedMovexClient,
MovexClientMasterClockOffset,
} from '../core-types';
import { MovexMasterContext } from '../masterContext';
// import { MovexMasterContext } from '../reducer';

export type IOEvents<
TState = unknown,
Expand Down Expand Up @@ -72,18 +73,22 @@ export type IOEvents<
rid: ResourceIdentifier<TResourceType>;
action: ActionOrActionTupleFromAction<A>;
}) => IOPayloadResult<
| {
type: 'ack';
nextChecksum: Checksum;
}
| {
type: 'masterActionAck';
// nextChecksum: Checksum;
nextCheckedAction: ToCheckedAction<A>;
}
| ({
type: 'reconciliation';
} & CheckedReconciliatoryActions<A>),
(
| {
type: 'ack';
nextChecksum: Checksum;
}
| {
type: 'masterActionAck';
// nextChecksum: Checksum;
nextCheckedAction: ToCheckedAction<A>;
}
| ({
type: 'reconciliation';
} & CheckedReconciliatoryActions<A>)
) & {
masterContext: MovexMasterContext;
},
'MasterResourceInexistent' | string
>; // Type the other errors

Expand All @@ -92,21 +97,25 @@ export type IOEvents<
* */
onReady: (p: SanitizedMovexClient) => void;

onClockSync: (p: undefined) => IOPayloadResult<number, unknown>; // acknowledges the client timestamp
// onClockSync: (p: undefined) => IOPayloadResult<number, unknown>; // acknowledges the client timestamp

onFwdAction: (
payload: {
rid: ResourceIdentifier<TResourceType>;
masterContext: MovexMasterContext;
} & ToCheckedAction<A>
) => IOPayloadResult<void, unknown>;
onReconciliateActions: (
payload: {
rid: ResourceIdentifier<TResourceType>;
masterContext: MovexMasterContext;
} & CheckedReconciliatoryActions<A>
) => IOPayloadResult<void, unknown>;
onResourceSubscriberAdded: (p: {
rid: ResourceIdentifier<TResourceType>;
client: SanitizedMovexClient;
// TODO: Make required after it works
masterContext: MovexMasterContext;
// clientId: MovexClient['id'];
}) => IOPayloadResult<
void,
Expand Down
8 changes: 0 additions & 8 deletions libs/movex-core-util/src/lib/core-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,8 @@ export type MovexClient<Info extends MovexClientInfo = UnknownRecord> = {
>;
};

export type MovexClientMasterClockOffset = number;

export type SanitizedMovexClient<Info extends UnknownRecord = UnknownRecord> =
Pick<MovexClient<Info>, 'id' | 'info'> & {
/**
* This is the diff between client and master needed to be adjusted on the client side
*
* TODO: Still not sure it should be available here - meaning all the peers can read it!
*/
clockOffset: MovexClientMasterClockOffset;
};

export type ResourceIdentifierObj<TResourceType extends string> = {
Expand Down
20 changes: 20 additions & 0 deletions libs/movex-core-util/src/lib/masterContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export enum MovexMasterContextMap {
requestAt = '$rqstAt$',
// Can add more such as "lag"
}

export type MovexMasterContext = {
requestAt: number; // timestamp
};

export type MovexMasterContextAsQuery = {
requestAt: () => number;
};

export const masterContextQuery: MovexMasterContextAsQuery = {
requestAt: () => MovexMasterContextMap['requestAt'] as unknown as number,
};

export const localMasterContextQuery: MovexMasterContextAsQuery = {
requestAt: () => new Date().getTime(),
};
17 changes: 0 additions & 17 deletions libs/movex-core-util/src/lib/masterQueries.ts

This file was deleted.

1 change: 1 addition & 0 deletions libs/movex-core-util/src/lib/public-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type MovexLogger = {
};



export type MovexDispatchOf<A extends AnyAction> = (
action: ToPublicAction<A> // TODO: Should this be ToPublic??
) => void;
Expand Down
11 changes: 6 additions & 5 deletions libs/movex-core-util/src/lib/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
ActionsCollectionMapBase,
AnyAction,
} from './action';
import { MovexMasterContext } from './masterContext';

export type MovexReducerFromActionsMap<
TState,
Expand All @@ -32,11 +33,11 @@ export type MovexReducerMap<
// TAction extends AnyAction = AnyAction
// > = (state: TState, action: TAction) => TState;

export type MovexMasterContext = {
// @Deprecate in favor of requestAt Props which enables purity
now: () => number; // timestamp
requestAt: number; // timestamp
};
// export type MovexMasterContext = {
// // @Deprecate in favor of requestAt Props which enables purity
// // now: () => number; // timestamp
// requestAt: number; // timestamp
// };

export type MovexReducer<S = any, A extends AnyAction = AnyAction> = ((
state: S,
Expand Down
2 changes: 1 addition & 1 deletion libs/movex-master/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "movex-master",
"version": "0.1.6-45",
"version": "0.1.6-58",
"license": "MIT",
"description": "Movex-master defines the master that wil be used on movex-server and movex-react-local-master",
"author": {
Expand Down
44 changes: 1 addition & 43 deletions libs/movex-master/src/lib/ConnectionToClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,12 @@ export class ConnectionToClient<
TResourceType extends string,
TClientInfo extends MovexClientInfo
> {
// public latencyMs: number = 0;

// public clientClockOffset: number = 0;

constructor(
public emitter: EventEmitter<IOEvents<TState, TAction, TResourceType>>,
public client: SanitizedMovexClient<TClientInfo>
) {}

async setReady() {
await this.syncClocks();

setReady() {
this.emitter.emit('onReady', this.client);
}

async syncClocks() {
const requestAt = new Date().getTime();

// console.log('Sync clock', this.client.id, { requestAt });

return this.emitter
.emitAndAcknowledge('onClockSync', undefined)
.then((res) => {
if (res.err) {
// console.log('Sync clock err', this.client.id);
console.error(res.err);
return;
}

// TODO: This might not be correct - also not sure if this
// it is roughly based on the NTP protocol as described here https://stackoverflow.com/a/15785110/2093626
// but adjusted for movex - the math might be wrong
// this.latencyMs = requestTime / 2;

const responseAt = new Date().getTime();
const requestTime = responseAt - requestAt;
const clientTimeAtRequest = res.val;

this.client.clockOffset =
clientTimeAtRequest - new Date().getTime() - requestTime;

// console.log('Sync clock ok', this.client.id, {
// requestAt,
// responseAt,
// requestTime,
// clientTimeAtRequest,
// clientClockOffset: this.client.clockOffset,
// });
});
}
}
8 changes: 4 additions & 4 deletions libs/movex-master/src/lib/MovexMasterResource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ test('applies public action', async () => {
};

const actual = await master
.applyAction(rid, clientAId, action, mockMasterContext)
.applyActionAndStateTransformer(rid, clientAId, action, mockMasterContext)
.resolveUnwrap();

const actualPublic = await master
Expand Down Expand Up @@ -114,7 +114,7 @@ test('applies only one private action w/o getting to reconciliation', async () =
};

const actual = await master
.applyAction(
.applyActionAndStateTransformer(
rid,
senderClientId,
[privateAction, publicAction],
Expand Down Expand Up @@ -197,7 +197,7 @@ test('applies private action UNTIL Reconciliation', async () => {

// White Private Action
const actualActionResultBeforeReconciliation = await master
.applyAction(
.applyActionAndStateTransformer(
rid,
whitePlayer,
[privateWhiteAction, publicWhiteAction],
Expand Down Expand Up @@ -285,7 +285,7 @@ test('applies private action UNTIL Reconciliation', async () => {

// Black Private Action (also the Reconciliatory Action)
const actualActionResultAfterReconciliation = await master
.applyAction(
.applyActionAndStateTransformer(
rid,
blackPlayer,
[privateBlackAction, publicBlackAction],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ test('gets initial state transformed with PrevState and Movex Context', async ()
prev: CounterState,
context: MovexMasterContext
): CounterState => {
return { count: context.now() };
return { count: context.requestAt };
};

const master = new MovexMasterResource(
Expand All @@ -108,7 +108,7 @@ test('gets initial state transformed with PrevState and Movex Context', async ()

MockDate.reset();

const expected = computeCheckedState({ count: MOCKED_NOW });
const expected = computeCheckedState({ count: 123 });

expect(actualPublic).toEqual(expected);
expect(actualClientSpecific).toEqual(expected);
Expand Down
Loading

0 comments on commit 0a528f1

Please sign in to comment.