Skip to content

Commit

Permalink
Persona / Launcher Integration (#18)
Browse files Browse the repository at this point in the history
* refactor(ri/client): fix up the interface for action creators

* feat(ri): hook ri up to launcher and persona

* fix(ri): use 31337 as local chain id everywhere

* feat: integrate persona and player entity when spawning

* feat: join game with persona integrated

* fix(solecs,ri): fix remappings

* fix(ri/client): add lodash to ri client

* feat(contract): check entity owner when moving

* fix(ri/contracts): fix Diamond upgrade hardhat task and make it faster

* fix(ri/contract): remove garbage function

Co-authored-by: ludens <ludens@lattice.xyz>
  • Loading branch information
Kooshaba and ludns committed Jun 22, 2022
1 parent aee8581 commit 1559577
Show file tree
Hide file tree
Showing 33 changed files with 300 additions and 188 deletions.
7 changes: 5 additions & 2 deletions packages/ecs-browser/src/ComponentEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { getComponentValueStrict, Layers, removeComponent } from "@latticexyz/recs";
import { getComponentValue, Layers, removeComponent } from "@latticexyz/recs";
import { AnyComponent, Entity, Schema } from "@latticexyz/recs/src/types";
import { observer } from "mobx-react-lite";
import { ComponentBrowserButton, ComponentEditorContainer, ComponentTitle } from "./StyledComponents";
Expand All @@ -18,6 +18,9 @@ export const ComponentEditor = observer(
layers: Layers;
setContractComponentValue: SetContractComponentFunction<Schema>;
}) => {
const componentValue = getComponentValue(component, entity);
if(!componentValue) return <></>;

return (
<ComponentEditorContainer>
<ComponentTitle>
Expand All @@ -27,7 +30,7 @@ export const ComponentEditor = observer(
<ComponentValueEditor
entity={entity}
component={component}
componentValue={getComponentValueStrict(component, entity)}
componentValue={componentValue}
layers={layers}
setContractComponentValue={setContractComponentValue}
/>
Expand Down
7 changes: 4 additions & 3 deletions packages/network/src/createTxQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,10 @@ export function createTxQueue<C extends Contracts>(
);
}

// TODO: only set gasPrice to 0 on local chain
// (https://linear.app/latticexyz/issue/LAT-587/integrate-mud-reference-implementation-with-launcher)
const result = await member(...argsWithoutOverrides, { ...overrides, nonce });
const configOverrides = { ...overrides, nonce };
if (network.config.chainId === 31337) configOverrides.gasPrice = 0;

const result = await member(...argsWithoutOverrides, configOverrides);
resolve(result);
return result;
} catch (e) {
Expand Down
10 changes: 5 additions & 5 deletions packages/recs/src/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ export function NotValue<T extends Schema>(
return { type: QueryFragmentType.NotValue, component, value };
}

export function ProxyRead(component: Component<{ entity: Type.Entity }>, depth: number): ProxyReadQueryFragment {
export function ProxyRead(component: Component<{ value: Type.Entity }>, depth: number): ProxyReadQueryFragment {
return { type: QueryFragmentType.ProxyRead, component, depth };
}

export function ProxyExpand(component: Component<{ entity: Type.Entity }>, depth: number): ProxyExpandQueryFragment {
export function ProxyExpand(component: Component<{ value: Type.Entity }>, depth: number): ProxyExpandQueryFragment {
return { type: QueryFragmentType.ProxyExpand, component, depth };
}

Expand Down Expand Up @@ -88,7 +88,7 @@ function passesQueryFragmentProxy<T extends Schema>(
if (!value) return null;

// Move up the proxy chain
proxyEntity = value.entity;
proxyEntity = value.value;
passes = passesQueryFragment(proxyEntity, fragment);

if (isBreakingPassState(passes, fragment)) {
Expand Down Expand Up @@ -130,12 +130,12 @@ function isBreakingPassState(passes: boolean, fragment: EntityQueryFragment<Sche
*/
export function getChildEntities(
entity: Entity,
component: Component<{ entity: Type.Entity }>,
component: Component<{ value: Type.Entity }>,
depth: number
): Set<Entity> {
if (depth === 0) return new Set();

const directChildEntities = getEntitiesWithValue(component, { entity });
const directChildEntities = getEntitiesWithValue(component, { value: entity });
if (depth === 1) return directChildEntities;

const indirectChildEntities = [...directChildEntities]
Expand Down
2 changes: 2 additions & 0 deletions packages/recs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export {
Has,
Not,
HasValue,
ProxyExpand,
ProxyRead,
} from "./Query";
export { Type } from "./constants";
export type {
Expand Down
4 changes: 2 additions & 2 deletions packages/recs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ export type NotValueQueryFragment<T extends Schema> = {

export type ProxyReadQueryFragment = {
type: QueryFragmentType.ProxyRead;
component: Component<{ entity: Type.Entity }>;
component: Component<{ value: Type.Entity }>;
depth: number;
};

export type ProxyExpandQueryFragment = {
type: QueryFragmentType.ProxyExpand;
component: Component<{ entity: Type.Entity }>;
component: Component<{ value: Type.Entity }>;
depth: number;
};

Expand Down
72 changes: 36 additions & 36 deletions packages/recs/tests/Query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ describe("Query", () => {

let CanMove: Component<Record<string, never>>;

let OwnedByEntity: Component<{ entity: Type.Entity }>;
let OwnedByEntity: Component<{ value: Type.Entity }>;

let Prototype: Component<Record<string, never>>;

let FromPrototype: Component<{ entity: Type.Entity }>;
let FromPrototype: Component<{ value: Type.Entity }>;

let Name: Component<{ name: Type.String }>;

Expand All @@ -41,9 +41,9 @@ describe("Query", () => {
Position = defineComponent(world, { x: Type.Number, y: Type.Number });
CanMove = defineComponent(world, {});
Name = defineComponent(world, { name: Type.String });
OwnedByEntity = defineComponent(world, { entity: Type.Entity });
OwnedByEntity = defineComponent(world, { value: Type.Entity });
Prototype = defineComponent(world, {});
FromPrototype = defineComponent(world, { entity: Type.Entity });
FromPrototype = defineComponent(world, { value: Type.Entity });
});

describe("defineQuery", () => {
Expand Down Expand Up @@ -93,48 +93,48 @@ describe("Query", () => {

it("should return all player owned entities up to the given depth", () => {
const Player = createEntity(world);
const Depth1 = createEntity(world, [withValue(OwnedByEntity, { entity: Player })]);
const Depth2 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth1 })]);
const Depth3 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth2 })]);
const Depth4 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth3 })]);
const Depth5 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth4 })]);
const Depth1 = createEntity(world, [withValue(OwnedByEntity, { value: Player })]);
const Depth2 = createEntity(world, [withValue(OwnedByEntity, { value: Depth1 })]);
const Depth3 = createEntity(world, [withValue(OwnedByEntity, { value: Depth2 })]);
const Depth4 = createEntity(world, [withValue(OwnedByEntity, { value: Depth3 })]);
const Depth5 = createEntity(world, [withValue(OwnedByEntity, { value: Depth4 })]);

expect(defineQuery([HasValue(OwnedByEntity, { entity: Player })]).get()).toEqual(new Set([Depth1]));
expect(defineQuery([HasValue(OwnedByEntity, { value: Player })]).get()).toEqual(new Set([Depth1]));

expect(defineQuery([ProxyExpand(OwnedByEntity, 0), HasValue(OwnedByEntity, { entity: Player })]).get()).toEqual(
expect(defineQuery([ProxyExpand(OwnedByEntity, 0), HasValue(OwnedByEntity, { value: Player })]).get()).toEqual(
new Set([Depth1])
);

expect(defineQuery([ProxyExpand(OwnedByEntity, 1), HasValue(OwnedByEntity, { entity: Player })]).get()).toEqual(
expect(defineQuery([ProxyExpand(OwnedByEntity, 1), HasValue(OwnedByEntity, { value: Player })]).get()).toEqual(
new Set([Depth1, Depth2])
);

expect(defineQuery([ProxyExpand(OwnedByEntity, 2), HasValue(OwnedByEntity, { entity: Player })]).get()).toEqual(
expect(defineQuery([ProxyExpand(OwnedByEntity, 2), HasValue(OwnedByEntity, { value: Player })]).get()).toEqual(
new Set([Depth1, Depth2, Depth3])
);

expect(defineQuery([ProxyExpand(OwnedByEntity, 3), HasValue(OwnedByEntity, { entity: Player })]).get()).toEqual(
expect(defineQuery([ProxyExpand(OwnedByEntity, 3), HasValue(OwnedByEntity, { value: Player })]).get()).toEqual(
new Set([Depth1, Depth2, Depth3, Depth4])
);

expect(defineQuery([ProxyExpand(OwnedByEntity, 4), HasValue(OwnedByEntity, { entity: Player })]).get()).toEqual(
expect(defineQuery([ProxyExpand(OwnedByEntity, 4), HasValue(OwnedByEntity, { value: Player })]).get()).toEqual(
new Set([Depth1, Depth2, Depth3, Depth4, Depth5])
);

expect(
defineQuery([
ProxyExpand(OwnedByEntity, Number.MAX_SAFE_INTEGER),
HasValue(OwnedByEntity, { entity: Player }),
HasValue(OwnedByEntity, { value: Player }),
]).get()
).toEqual(new Set([Depth1, Depth2, Depth3, Depth4, Depth5]));
});

it("should return entites owned by an entity with Name component Alice", () => {
const Player = createEntity(world, [withValue(Name, { name: "Alice" })]);
const Depth1 = createEntity(world, [withValue(OwnedByEntity, { entity: Player })]);
const Depth2 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth1 })]);
const Depth3 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth2 })]);
const Depth4 = createEntity(world, [withValue(OwnedByEntity, { entity: Depth3 })]);
const Depth1 = createEntity(world, [withValue(OwnedByEntity, { value: Player })]);
const Depth2 = createEntity(world, [withValue(OwnedByEntity, { value: Depth1 })]);
const Depth3 = createEntity(world, [withValue(OwnedByEntity, { value: Depth2 })]);
const Depth4 = createEntity(world, [withValue(OwnedByEntity, { value: Depth3 })]);

expect(
defineQuery(
Expand Down Expand Up @@ -199,12 +199,12 @@ describe("Query", () => {
const proto = createEntity(world, [withValue(Prototype, {}), withValue(CanMove, {})]);

const instance1 = createEntity(world, [
withValue(FromPrototype, { entity: proto }),
withValue(FromPrototype, { value: proto }),
withValue(Position, { x: 1, y: 1 }),
]);

const instance2 = createEntity(world, [
withValue(FromPrototype, { entity: proto }),
withValue(FromPrototype, { value: proto }),
withValue(Position, { x: 1, y: 1 }),
]);

Expand All @@ -226,9 +226,9 @@ describe("Query", () => {
it("should return all entities with Position component that can't move", () => {
const proto = createEntity(world, [withValue(Prototype, {}), withValue(CanMove, {})]);

createEntity(world, [withValue(FromPrototype, { entity: proto }), withValue(Position, { x: 1, y: 1 })]);
createEntity(world, [withValue(FromPrototype, { value: proto }), withValue(Position, { x: 1, y: 1 })]);

createEntity(world, [withValue(FromPrototype, { entity: proto }), withValue(Position, { x: 1, y: 1 })]);
createEntity(world, [withValue(FromPrototype, { value: proto }), withValue(Position, { x: 1, y: 1 })]);

const entity3 = createEntity(world, [withValue(Position, { x: 1, y: 1 })]);

Expand All @@ -243,47 +243,47 @@ describe("Query", () => {

// Instance 1
createEntity(world, [
withValue(FromPrototype, { entity: Proto1 }),
withValue(OwnedByEntity, { entity: Player1 }),
withValue(FromPrototype, { value: Proto1 }),
withValue(OwnedByEntity, { value: Player1 }),
withValue(Position, { x: 1, y: 1 }),
]);

// Instance 2
createEntity(world, [
withValue(FromPrototype, { entity: Proto2 }),
withValue(OwnedByEntity, { entity: Player1 }),
withValue(FromPrototype, { value: Proto2 }),
withValue(OwnedByEntity, { value: Player1 }),
withValue(Position, { x: 1, y: 1 }),
]);

const Instance3 = createEntity(world, [
withValue(FromPrototype, { entity: Proto1 }),
withValue(OwnedByEntity, { entity: Player2 }),
withValue(FromPrototype, { value: Proto1 }),
withValue(OwnedByEntity, { value: Player2 }),
withValue(Position, { x: 1, y: 1 }),
]);

// Instance 4
createEntity(world, [
withValue(FromPrototype, { entity: Proto2 }),
withValue(OwnedByEntity, { entity: Player2 }),
withValue(FromPrototype, { value: Proto2 }),
withValue(OwnedByEntity, { value: Player2 }),
withValue(Position, { x: 1, y: 1 }),
]);

// Entity 5
createEntity(world, [withValue(OwnedByEntity, { entity: Player1 }), withValue(Position, { x: 1, y: 1 })]);
createEntity(world, [withValue(OwnedByEntity, { value: Player1 }), withValue(Position, { x: 1, y: 1 })]);

// Entity 6
createEntity(world, [withValue(OwnedByEntity, { entity: Player2 }), withValue(Position, { x: 1, y: 1 })]);
createEntity(world, [withValue(OwnedByEntity, { value: Player2 }), withValue(Position, { x: 1, y: 1 })]);

// Entity 7
createEntity(world, [
withValue(CanMove, {}),
withValue(OwnedByEntity, { entity: Player1 }),
withValue(OwnedByEntity, { value: Player1 }),
withValue(Position, { x: 1, y: 1 }),
]);

const Entity8 = createEntity(world, [
withValue(CanMove, {}),
withValue(OwnedByEntity, { entity: Player2 }),
withValue(OwnedByEntity, { value: Player2 }),
withValue(Position, { x: 1, y: 1 }),
]);

Expand Down
2 changes: 2 additions & 0 deletions packages/ri/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@latticexyz/utils": "^0.1.7",
"async-mutex": "^0.3.2",
"ethers": "^5.6.6",
"lodash": "^4.17.21",
"mobx": "^6.4.2",
"mobx-react-lite": "^3.3.0",
"mobx-utils": "^6.0.4",
Expand All @@ -35,6 +36,7 @@
"cypress": "^9.5.0",
"cypress-wait-until": "^1.7.2",
"eslint": "^8.9.0",
"lodash": "^4.17.21",
"jest": "^27.5.1",
"jest-canvas-mock": "^2.3.1",
"prettier": "^2.5.1",
Expand Down
18 changes: 17 additions & 1 deletion packages/ri/client/src/boot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,23 @@ async function bootLayers() {
} = {};

async function bootLayers() {
if (!layers.network) layers.network = await createNetworkLayer();
const params = new URLSearchParams(window.location.search);
const contractAddress = params.get("contractAddress");
const privateKey = params.get("burnerWalletPrivateKey");
const chainIdString = params.get("chainId");
const personaIdString = params.get("personaId");

let networkLayerConfig;
if (contractAddress && privateKey && chainIdString && personaIdString) {
networkLayerConfig = {
contractAddress,
privateKey,
chainId: parseInt(chainIdString),
personaId: parseInt(personaIdString),
};
}

if (!layers.network) layers.network = await createNetworkLayer(networkLayerConfig);
if (!layers.headless) layers.headless = await createHeadlessLayer(layers.network);
if (!layers.local) layers.local = await createLocalLayer(layers.headless);
if (!layers.phaser) layers.phaser = await createPhaserLayer(layers.local);
Expand Down
4 changes: 2 additions & 2 deletions packages/ri/client/src/layers/Headless/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { spawnCreatureFunc } from "./spawnCreature";
export { moveEntityFunc } from "./moveEntity";
export { joinGame } from "./joinGame";
export { moveEntity } from "./moveEntity";
37 changes: 37 additions & 0 deletions packages/ri/client/src/layers/Headless/api/joinGame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { WorldCoord } from "@latticexyz/phaserx/src/types";
import { getEntitiesWithValue } from "@latticexyz/recs";
import { getPlayerEntity } from "@latticexyz/std-client";
import { NetworkLayer } from "../../Network";
import { EntityTypes } from "../../Network/types";
import { ActionSystem } from "../types";

export function joinGame(network: NetworkLayer, actions: ActionSystem, targetPosition: WorldCoord) {
const { Position, Persona } = network.components;

actions.add({
id: `spawn ${Math.random()}`,
components: { Position, Persona },
requirement: ({ Position, Persona }) => {
const blockingEntities = getEntitiesWithValue(Position, targetPosition);
if (blockingEntities.size !== 0) return null;

if (!network.personaId) {
console.warn("No persona ID found, canceling spawn attempt");
return null;
}

const playerEntity = getPlayerEntity(Persona, network.personaId);
if (playerEntity) {
console.warn("Player already spawned, canceling spawn.");
return null;
}

return true;
},
updates: () => [],
execute: () => {
console.log(`spawning entityType: ${EntityTypes.Creature} at ${JSON.stringify(targetPosition)}`);
network.api.joinGame(targetPosition, EntityTypes.Creature);
},
});
}
Loading

0 comments on commit 1559577

Please sign in to comment.