diff --git a/packages/client/package.json b/packages/client/package.json
index ed07f2af..b9c3560c 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -27,11 +27,13 @@
"contracts": "workspace:*",
"dayjs": "^1.11.9",
"ethers": "^5.7.2",
+ "lodash": "^4.17.21",
"postcss": "^8.4.28",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rxjs": "7.5.5",
"tailwindcss": "^3.3.3",
+ "use-resize-observer": "^9.1.0",
"viem": "1.6.0"
},
"devDependencies": {
diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx
index dc95c026..b98a6dc2 100644
--- a/packages/client/src/App.tsx
+++ b/packages/client/src/App.tsx
@@ -1,5 +1,6 @@
import { useComponentValue } from "@latticexyz/react";
import { useMUD } from "./MUDContext";
+import { PhaserLayer } from "./PhaserLayer";
import AutoChess from "./ui/ChessMain";
import JoinGame from "./ui/JoinGame";
import "./index.css";
@@ -19,6 +20,7 @@ export const App = () => {
return (
<>
+
{isPlay ? : }
diff --git a/packages/client/src/LoadingScreen/BootScreen.tsx b/packages/client/src/LoadingScreen/BootScreen.tsx
new file mode 100644
index 00000000..0e5b7a00
--- /dev/null
+++ b/packages/client/src/LoadingScreen/BootScreen.tsx
@@ -0,0 +1,42 @@
+import React from "react";
+import styled from "styled-components";
+
+type Props = {
+ children: React.ReactNode;
+};
+
+export const BootScreen = ({ children }: Props) => {
+ return (
+
+
+ <>{children || <> >}>
+
+
+ );
+};
+
+const Container = styled.div`
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ background-color: rgb(0 0 0 / 100%);
+ display: grid;
+ align-content: center;
+ align-items: center;
+ justify-content: center;
+ justify-items: center;
+ transition: all 2s ease;
+ grid-gap: 50px;
+ z-index: 100;
+ pointer-events: none;
+ color: white;
+
+ div {
+ font-family: "Lattice Pixel", sans-serif;
+ }
+
+ img {
+ transition: all 2s ease;
+ width: 100px;
+ }
+`;
diff --git a/packages/client/src/LoadingScreen/LoadingBar.tsx b/packages/client/src/LoadingScreen/LoadingBar.tsx
new file mode 100644
index 00000000..2c05420a
--- /dev/null
+++ b/packages/client/src/LoadingScreen/LoadingBar.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import styled from "styled-components";
+
+export const LoadingBar: React.FC<{
+ percentage: number;
+ className?: string;
+}> = ({ percentage, className }) => {
+ return (
+
+
+
+ );
+};
+
+const Wrapper = styled.div`
+ position: relative;
+ height: 4px;
+ background-color: #2a2a2a;
+`;
+const Inner = styled.div<{ percentage: number }>`
+ height: 100%;
+ width: ${(p) => p.percentage}%;
+ background-color: #fff;
+`;
diff --git a/packages/client/src/LoadingScreen/LoadingScreen.tsx b/packages/client/src/LoadingScreen/LoadingScreen.tsx
new file mode 100644
index 00000000..3f427369
--- /dev/null
+++ b/packages/client/src/LoadingScreen/LoadingScreen.tsx
@@ -0,0 +1,54 @@
+import React from "react";
+import styled from "styled-components";
+import { LoadingBar } from "./LoadingBar";
+import { BootScreen } from "./BootScreen";
+import { useComponentValue } from "@latticexyz/react";
+import { useMUD } from "../../store";
+import { singletonEntity } from "@latticexyz/store-sync/recs";
+import { SyncStep } from "@latticexyz/store-sync";
+
+export const LoadingScreen = () => {
+ const {
+ networkLayer: {
+ components: { SyncProgress },
+ },
+ } = useMUD();
+
+ const syncProgress = useComponentValue(SyncProgress, singletonEntity, {
+ message: "Connecting",
+ percentage: 0,
+ step: SyncStep.INITIALIZE,
+ latestBlockNumber: 0n,
+ lastBlockNumberProcessed: 0n,
+ });
+
+ if (syncProgress.step === SyncStep.LIVE) {
+ return null;
+ }
+
+ return (
+
+ {syncProgress.message}…
+
+ {Math.floor(syncProgress.percentage)}%
+
+
+
+ );
+};
+
+const LoadingContainer = styled.div`
+ display: grid;
+ justify-items: start;
+ justify-content: start;
+ align-items: center;
+ height: 30px;
+ width: 100%;
+ grid-gap: 20px;
+ grid-template-columns: auto 1fr;
+`;
+
+const Loading = styled(LoadingBar)`
+ width: 100%;
+ min-width: 200px;
+`;
diff --git a/packages/client/src/LoadingScreen/index.ts b/packages/client/src/LoadingScreen/index.ts
new file mode 100644
index 00000000..ccaf5245
--- /dev/null
+++ b/packages/client/src/LoadingScreen/index.ts
@@ -0,0 +1 @@
+export * from "./LoadingScreen";
diff --git a/packages/client/src/PhaserLayer.tsx b/packages/client/src/PhaserLayer.tsx
new file mode 100644
index 00000000..54f5546b
--- /dev/null
+++ b/packages/client/src/PhaserLayer.tsx
@@ -0,0 +1,20 @@
+import React, { useEffect } from "react";
+import { NetworkLayer } from "../layers/network/createNetworkLayer";
+import { useStore } from "../store";
+import { usePhaserLayer } from "./hooks/usePhaserLayer";
+
+type Props = {
+ networkLayer: NetworkLayer | null;
+};
+
+export const PhaserLayer = ({ networkLayer }: Props) => {
+ const { ref: phaserRef, phaserLayer } = usePhaserLayer({ networkLayer });
+
+ useEffect(() => {
+ if (phaserLayer) {
+ useStore.setState({ phaserLayer });
+ }
+ }, [phaserLayer]);
+
+ return
;
+};
diff --git a/packages/client/src/artTypes/world.ts b/packages/client/src/artTypes/world.ts
new file mode 100644
index 00000000..ba827ee5
--- /dev/null
+++ b/packages/client/src/artTypes/world.ts
@@ -0,0 +1,9 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+export enum Tileset {
+ Grass = 0,
+ Mountain = 1,
+ Forest = 2,
+}
+export enum TileAnimationKey {}
+export const TileAnimations: { [key in TileAnimationKey]: number[] } = {};
diff --git a/packages/client/src/hooks/useNetworkLayer.tsx b/packages/client/src/hooks/useNetworkLayer.tsx
new file mode 100644
index 00000000..e2e13ab0
--- /dev/null
+++ b/packages/client/src/hooks/useNetworkLayer.tsx
@@ -0,0 +1,17 @@
+import { useEffect, useMemo } from "react";
+import { createNetworkLayer } from "../../layers/network/createNetworkLayer";
+import { usePromiseValue } from "./usePromiseValue";
+
+export const useNetworkLayer = () => {
+ const networkLayerPromise = useMemo(() => {
+ return createNetworkLayer();
+ }, []);
+
+ useEffect(() => {
+ return () => {
+ networkLayerPromise.then((networkLayer) => networkLayer.world.dispose());
+ };
+ }, [networkLayerPromise]);
+
+ return usePromiseValue(networkLayerPromise);
+};
diff --git a/packages/client/src/hooks/usePhaserLayer.tsx b/packages/client/src/hooks/usePhaserLayer.tsx
new file mode 100644
index 00000000..298b274f
--- /dev/null
+++ b/packages/client/src/hooks/usePhaserLayer.tsx
@@ -0,0 +1,93 @@
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
+import useResizeObserver, { ResizeHandler } from "use-resize-observer";
+import { throttle } from "lodash";
+import { createPhaserLayer } from "../layers/phaser/createPhaserLayer";
+import { NetworkLayer } from "../layers/network/createNetworkLayer";
+import { usePromiseValue } from "./usePromiseValue";
+import { phaserConfig } from "../layers/phaser/configurePhaser";
+
+const createContainer = () => {
+ const container = document.createElement("div");
+ container.style.width = "100vw";
+ container.style.height = "100vh";
+ container.style.pointerEvents = "all";
+ container.style.overflow = "hidden";
+ return container;
+};
+
+type Props = {
+ networkLayer: NetworkLayer | null;
+};
+
+export const usePhaserLayer = ({ networkLayer }: Props) => {
+ const parentRef = useRef
(null);
+ const [{ width, height }, setSize] = useState({ width: 0, height: 0 });
+
+ const { phaserLayerPromise, container } = useMemo(() => {
+ if (!networkLayer) return { phaserLayerPromise: null, container: null };
+
+ const container = createContainer();
+ if (parentRef.current) {
+ parentRef.current.appendChild(container);
+ }
+
+ return {
+ container,
+ phaserLayerPromise: createPhaserLayer(networkLayer, {
+ ...phaserConfig,
+ scale: {
+ ...phaserConfig.scale,
+ parent: container,
+ mode: Phaser.Scale.NONE,
+ width,
+ height,
+ },
+ }),
+ };
+
+ // We don't want width/height to recreate phaser layer, so we ignore linter
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [networkLayer]);
+
+ useEffect(() => {
+ return () => {
+ phaserLayerPromise?.then((phaserLayer) => phaserLayer.world.dispose());
+ container?.remove();
+ };
+ }, [container, phaserLayerPromise]);
+
+ const phaserLayer = usePromiseValue(phaserLayerPromise);
+
+ const onResize = useMemo(() => {
+ return throttle(({ width, height }) => {
+ setSize({ width: width ?? 0, height: height ?? 0 });
+ }, 500);
+ }, []);
+
+ useResizeObserver({
+ ref: container,
+ onResize,
+ });
+
+ const ref = useCallback(
+ (el: HTMLElement | null) => {
+ parentRef.current = el;
+ if (container) {
+ if (parentRef.current) {
+ parentRef.current.appendChild(container);
+ } else {
+ container.remove();
+ }
+ }
+ },
+ [container]
+ );
+
+ return useMemo(() => ({ ref, phaserLayer }), [ref, phaserLayer]);
+};
diff --git a/packages/client/src/hooks/usePromiseValue.ts b/packages/client/src/hooks/usePromiseValue.ts
new file mode 100644
index 00000000..bd71492d
--- /dev/null
+++ b/packages/client/src/hooks/usePromiseValue.ts
@@ -0,0 +1,24 @@
+import { useEffect, useState, useRef } from "react";
+
+export const usePromiseValue = (promise: Promise | null) => {
+ const promiseRef = useRef(promise);
+ const [value, setValue] = useState(null);
+ useEffect(() => {
+ if (!promise) return;
+ let isMounted = true;
+ promiseRef.current = promise;
+ // TODO: do something with promise errors?
+ promise.then((resolvedValue) => {
+ // skip if unmounted (state changes will cause errors otherwise)
+ if (!isMounted) return;
+ // If our promise was replaced before it resolved, ignore the result
+ if (promiseRef.current !== promise) return;
+
+ setValue(resolvedValue);
+ });
+ return () => {
+ isMounted = false;
+ };
+ }, [promise]);
+ return value;
+};
diff --git a/packages/client/src/layers/network/createNetworkLayer.ts b/packages/client/src/layers/network/createNetworkLayer.ts
new file mode 100644
index 00000000..2904fce3
--- /dev/null
+++ b/packages/client/src/layers/network/createNetworkLayer.ts
@@ -0,0 +1,15 @@
+import { world } from "../../mud/world";
+import { setup } from "../../mud/setup";
+
+export type NetworkLayer = Awaited>;
+
+export const createNetworkLayer = async () => {
+ const { components, systemCalls, network } = await setup();
+
+ return {
+ world,
+ systemCalls,
+ components,
+ network,
+ };
+};
diff --git a/packages/client/src/layers/phaser/configurePhaser.ts b/packages/client/src/layers/phaser/configurePhaser.ts
new file mode 100644
index 00000000..30359b87
--- /dev/null
+++ b/packages/client/src/layers/phaser/configurePhaser.ts
@@ -0,0 +1,99 @@
+import {
+ defineSceneConfig,
+ AssetType,
+ defineScaleConfig,
+ defineMapConfig,
+ defineCameraConfig,
+} from "@latticexyz/phaserx";
+import worldTileset from "../../../public/assets/tilesets/world.png";
+import { TileAnimations, Tileset } from "../../artTypes/world";
+import {
+ Sprites,
+ Assets,
+ Maps,
+ Scenes,
+ TILE_HEIGHT,
+ TILE_WIDTH,
+ Animations,
+} from "./constants";
+
+const ANIMATION_INTERVAL = 200;
+
+const mainMap = defineMapConfig({
+ chunkSize: TILE_WIDTH * 64, // tile size * tile amount
+ tileWidth: TILE_WIDTH,
+ tileHeight: TILE_HEIGHT,
+ backgroundTile: [Tileset.Grass],
+ animationInterval: ANIMATION_INTERVAL,
+ tileAnimations: TileAnimations,
+ layers: {
+ layers: {
+ Background: { tilesets: ["Default"] },
+ Foreground: { tilesets: ["Default"] },
+ },
+ defaultLayer: "Background",
+ },
+});
+
+export const phaserConfig = {
+ sceneConfig: {
+ [Scenes.Main]: defineSceneConfig({
+ assets: {
+ [Assets.Tileset]: {
+ type: AssetType.Image,
+ key: Assets.Tileset,
+ path: worldTileset,
+ },
+ [Assets.MainAtlas]: {
+ type: AssetType.MultiAtlas,
+ key: Assets.MainAtlas,
+ // Add a timestamp to the end of the path to prevent caching
+ path: `/assets/atlases/atlas.json?timestamp=${Date.now()}`,
+ options: {
+ imagePath: "/assets/atlases/",
+ },
+ },
+ },
+ maps: {
+ [Maps.Main]: mainMap,
+ },
+ sprites: {
+ [Sprites.Soldier]: {
+ assetKey: Assets.MainAtlas,
+ frame: "sprites/soldier/idle/0.png",
+ },
+ },
+ animations: [
+ {
+ key: Animations.SwordsmanIdle,
+ assetKey: Assets.MainAtlas,
+ startFrame: 0,
+ endFrame: 3,
+ frameRate: 6,
+ repeat: -1,
+ prefix: "sprites/soldier/idle/",
+ suffix: ".png",
+ },
+ ],
+ tilesets: {
+ Default: {
+ assetKey: Assets.Tileset,
+ tileWidth: TILE_WIDTH,
+ tileHeight: TILE_HEIGHT,
+ },
+ },
+ }),
+ },
+ scale: defineScaleConfig({
+ parent: "phaser-game",
+ zoom: 1,
+ mode: Phaser.Scale.NONE,
+ }),
+ cameraConfig: defineCameraConfig({
+ pinchSpeed: 1,
+ wheelSpeed: 1,
+ maxZoom: 3,
+ minZoom: 1,
+ }),
+ cullingChunkSize: TILE_HEIGHT * 16,
+};
diff --git a/packages/client/src/layers/phaser/constants.ts b/packages/client/src/layers/phaser/constants.ts
new file mode 100644
index 00000000..61c8d674
--- /dev/null
+++ b/packages/client/src/layers/phaser/constants.ts
@@ -0,0 +1,22 @@
+export enum Scenes {
+ Main = "Main",
+}
+
+export enum Maps {
+ Main = "Main",
+}
+
+export enum Animations {
+ SwordsmanIdle = "SwordsmanIdle",
+}
+export enum Sprites {
+ Soldier,
+}
+
+export enum Assets {
+ MainAtlas = "MainAtlas",
+ Tileset = "Tileset",
+}
+
+export const TILE_HEIGHT = 32;
+export const TILE_WIDTH = 32;
diff --git a/packages/client/src/layers/phaser/createPhaserLayer.ts b/packages/client/src/layers/phaser/createPhaserLayer.ts
new file mode 100644
index 00000000..48324830
--- /dev/null
+++ b/packages/client/src/layers/phaser/createPhaserLayer.ts
@@ -0,0 +1,40 @@
+import { createPhaserEngine } from "@latticexyz/phaserx";
+import { namespaceWorld } from "@latticexyz/recs";
+import { NetworkLayer } from "../network/createNetworkLayer";
+import { registerSystems } from "./systems";
+
+export type PhaserLayer = Awaited>;
+type PhaserEngineConfig = Parameters[0];
+
+export const createPhaserLayer = async (
+ networkLayer: NetworkLayer,
+ phaserConfig: PhaserEngineConfig
+) => {
+ const world = namespaceWorld(networkLayer.world, "phaser");
+
+ const {
+ game,
+ scenes,
+ dispose: disposePhaser,
+ } = await createPhaserEngine(phaserConfig);
+ world.registerDisposer(disposePhaser);
+
+ const { camera } = scenes.Main;
+
+ camera.phaserCamera.setBounds(-1000, -1000, 2000, 2000);
+ camera.phaserCamera.centerOn(0, 0);
+
+ const components = {};
+
+ const layer = {
+ networkLayer,
+ world,
+ game,
+ scenes,
+ components,
+ };
+
+ registerSystems(layer);
+
+ return layer;
+};
diff --git a/packages/client/src/layers/phaser/systems/createCamera.ts b/packages/client/src/layers/phaser/systems/createCamera.ts
new file mode 100644
index 00000000..df742829
--- /dev/null
+++ b/packages/client/src/layers/phaser/systems/createCamera.ts
@@ -0,0 +1,13 @@
+import { PhaserLayer } from "../createPhaserLayer";
+
+export const createCamera = (layer: PhaserLayer) => {
+ const {
+ scenes: {
+ Main: {
+ camera: { phaserCamera },
+ },
+ },
+ } = layer;
+
+ phaserCamera.centerOn(0, 0);
+};
diff --git a/packages/client/src/layers/phaser/systems/createMapSystem.ts b/packages/client/src/layers/phaser/systems/createMapSystem.ts
new file mode 100644
index 00000000..f847f3ec
--- /dev/null
+++ b/packages/client/src/layers/phaser/systems/createMapSystem.ts
@@ -0,0 +1,32 @@
+import { Tileset } from "../../../artTypes/world";
+import { PhaserLayer } from "../createPhaserLayer";
+import { createNoise2D } from "simplex-noise";
+
+export function createMapSystem(layer: PhaserLayer) {
+ const {
+ scenes: {
+ Main: {
+ maps: {
+ Main: { putTileAt },
+ },
+ },
+ },
+ } = layer;
+
+ const noise = createNoise2D();
+
+ for (let x = -500; x < 500; x++) {
+ for (let y = -500; y < 500; y++) {
+ const coord = { x, y };
+ const seed = noise(x, y);
+
+ putTileAt(coord, Tileset.Grass, "Background");
+
+ if (seed >= 0.45) {
+ putTileAt(coord, Tileset.Mountain, "Foreground");
+ } else if (seed < -0.45) {
+ putTileAt(coord, Tileset.Forest, "Foreground");
+ }
+ }
+ }
+}
diff --git a/packages/client/src/layers/phaser/systems/index.ts b/packages/client/src/layers/phaser/systems/index.ts
new file mode 100644
index 00000000..7b5a4bd1
--- /dev/null
+++ b/packages/client/src/layers/phaser/systems/index.ts
@@ -0,0 +1 @@
+export * from "./registerSystems";
diff --git a/packages/client/src/layers/phaser/systems/registerSystems.ts b/packages/client/src/layers/phaser/systems/registerSystems.ts
new file mode 100644
index 00000000..5db82c68
--- /dev/null
+++ b/packages/client/src/layers/phaser/systems/registerSystems.ts
@@ -0,0 +1,8 @@
+import { PhaserLayer } from "../createPhaserLayer";
+import { createCamera } from "./createCamera";
+import { createMapSystem } from "./createMapSystem";
+
+export const registerSystems = (layer: PhaserLayer) => {
+ createCamera(layer);
+ createMapSystem(layer);
+};
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
new file mode 100644
index 00000000..051050d5
--- /dev/null
+++ b/packages/client/src/store.ts
@@ -0,0 +1,35 @@
+import { create } from "zustand";
+import { NetworkLayer } from "./layers/network/createNetworkLayer";
+import { PhaserLayer } from "./layers/phaser/createPhaserLayer";
+
+export type Store = {
+ networkLayer: NetworkLayer | null;
+ phaserLayer: PhaserLayer | null;
+ devMode: boolean;
+};
+
+export type UIStore = {
+ networkLayer: NetworkLayer;
+ phaserLayer: PhaserLayer;
+ devMode: boolean;
+};
+
+export const useStore = create(() => ({
+ networkLayer: null,
+ phaserLayer: null,
+ devMode: false,
+}));
+
+export const usePhaserMud = () => {
+ const { networkLayer, phaserLayer, devMode } = useStore();
+
+ if (networkLayer === null || phaserLayer === null) {
+ throw new Error("Store not initialized");
+ }
+
+ return {
+ networkLayer,
+ phaserLayer,
+ devMode,
+ } as UIStore;
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c5899d85..2a086cac 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -96,6 +96,9 @@ importers:
ethers:
specifier: ^5.7.2
version: 5.7.2
+ lodash:
+ specifier: ^4.17.21
+ version: 4.17.21
postcss:
specifier: ^8.4.28
version: 8.4.28
@@ -111,6 +114,9 @@ importers:
tailwindcss:
specifier: ^3.3.3
version: 3.3.3
+ use-resize-observer:
+ specifier: ^9.1.0
+ version: 9.1.0(react-dom@18.2.0)(react@18.2.0)
viem:
specifier: 1.6.0
version: 1.6.0(typescript@4.9.5)(zod@3.21.4)
@@ -1052,6 +1058,10 @@ packages:
'@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.4.15
+ /@juggle/resize-observer@3.4.0:
+ resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
+ dev: false
+
/@latticexyz/block-logs-stream@2.0.0-next.4(typescript@4.9.5)(zod@3.21.4):
resolution: {integrity: sha512-8QRMkkMhwxdAd6ifky2latt7qopcoHf3d3bGVz0pwdv48H3iZTK1xWRavf+chCHYddMuW+C8QprNU9wp/U19Zg==}
dependencies:
@@ -6282,6 +6292,17 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /use-resize-observer@9.1.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==}
+ peerDependencies:
+ react: 16.8.0 - 18
+ react-dom: 16.8.0 - 18
+ dependencies:
+ '@juggle/resize-observer': 3.4.0
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/use-sync-external-store@1.2.0(react@18.2.0):
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
@@ -6579,19 +6600,19 @@ packages:
dev: false
github.com/dapphub/ds-test/c9ce3f25bde29fc5eb9901842bf02850dfd2d084:
- resolution: {tarball: https://codeload.github.com/dapphub/ds-test/tar.gz/c9ce3f25bde29fc5eb9901842bf02850dfd2d084}
+ resolution: {commit: c9ce3f25bde29fc5eb9901842bf02850dfd2d084, repo: git+ssh://git@github.com/dapphub/ds-test.git, type: git}
name: ds-test
version: 1.0.0
dev: true
github.com/dk1a/memmove/ffd71cd77b1708574ef46a667b23ca3a5cc9fa27:
- resolution: {tarball: https://codeload.github.com/dk1a/memmove/tar.gz/ffd71cd77b1708574ef46a667b23ca3a5cc9fa27}
+ resolution: {commit: ffd71cd77b1708574ef46a667b23ca3a5cc9fa27, repo: git+ssh://git@github.com/dk1a/memmove.git, type: git}
name: memmove
version: 0.1.0
dev: true
github.com/transmissions11/solmate/9cf1428245074e39090dceacb0c28b1f684f584c:
- resolution: {tarball: https://codeload.github.com/transmissions11/solmate/tar.gz/9cf1428245074e39090dceacb0c28b1f684f584c}
+ resolution: {commit: 9cf1428245074e39090dceacb0c28b1f684f584c, repo: git+ssh://git@github.com/transmissions11/solmate.git, type: git}
name: solmate
version: 6.5.0
dev: true