Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[discuss] Codegen world interface #82

Closed
ponderingdemocritus opened this issue Dec 19, 2023 · 8 comments
Closed

[discuss] Codegen world interface #82

ponderingdemocritus opened this issue Dec 19, 2023 · 8 comments
Assignees
Labels
help wanted Extra attention is needed

Comments

@ponderingdemocritus
Copy link
Contributor

We want to minimize boilerplate writing as much as possible while also improving type safety and completion for the best possible developer experience.

One way we can achieve this is by generating a World Class based on the manifest.json, which builds a typed interface for all potential endpoints into the world.

Ideally, the end goal is something like this:

await dojo.play.create({ account, name: "ohayo"})

This would execute the Play system contract and the Create function.

We could achieve this by generating classes like:

(Slayer is the World Name)

export class Slayer extends RPCProvider {
    public play: PlayContract;

    constructor(worldAddress: string, manifest?: any, url?: string) {
        super(worldAddress, manifest, url);
       
        // each system contract would be nested like this
        this.play = new PlayContract(this);
    }
}

interface IPlayContractFunctions {
    create: (props: CreateProps) => Promise<any>;
    seek: (props: Signer) => Promise<any>;
    roll: (props: RollProps) => Promise<any>;
    buy: (props: BuyProps) => Promise<any>;
    apply: (props: ApplyProps) => Promise<any>;
}

export class PlayContract implements IPlayContractFunctions {
    private slayer: Slayer;
    private name: string;

    constructor(provider: Slayer) {
        this.slayer = provider;
        this.name = "play";
    }

    async create(props: CreateProps): Promise<any> {
        try {
            return await this.slayer.execute(
                props.account,
                this.name,
                "create",
                [this.slayer.getWorldAddress(), props.name]
            );
        } catch (error) {
            console.error("Error in creating entity:", error);
            throw error;
        }
    }
}

Since worlds will contain many contracts we need to nest the classes to avoid potential clashes.

This file could be generated by simply:

bun run generate-systems

Looking for feedback on this.

@tarrencev @broody @JunichiSugiura @rsodre @Matth26 @aymericdelab

@ponderingdemocritus ponderingdemocritus added the help wanted Extra attention is needed label Dec 19, 2023
@theskyvalker
Copy link

This functionality would be really awesome. As I am developing with the Dojo stack, I find myself wanting something like this more and more and I actually searched if this was already there for a bit - it makes a lot of sense to have this for developers.

@ponderingdemocritus
Copy link
Contributor Author

great! Yes, this will be the next big feature - I would suggest to just copy this structure into your project for now.

@Matth26
Copy link
Contributor

Matth26 commented Dec 24, 2023

Totally agree, after doing almost the same boilerplate for 3 games. Auto-gen with TypeScript would be awesome and definitely would speed up the dev process!

@rsodre
Copy link
Contributor

rsodre commented Dec 24, 2023

I like this, the more abstraction the better
Maybe the whole dojo client folder, including DojoContext could be moves to the react package as well

@aymericdelab
Copy link
Contributor

Ohayo, this would be a great feature indeed for lots of projets. Though for Eternum, we mostly use multi calls which would not be possible to automatically generate.

@ponderingdemocritus
Copy link
Contributor Author

Rethought this a bit. We should avoid classes and be purely functional.

Have been experimenting with interfaces and new structure in the react-app on the PR - #99

Have arrived at something like this which provides a typed interface and avoids strange class inheritence.

export async function setupWorld(provider: DojoProvider) {
    function actions() {
        const contract_name = "actions";

        const spawn = async ({ account }: { account: Account }) => {
            try {
                return await provider.execute(
                    account,
                    contract_name,
                    "spawn",
                    []
                );
            } catch (error) {
                console.error("Error executing spawn:", error);
                throw error;
            }
        };

        const move = async ({
            account,
            direction,
        }: {
            account: Account;
            direction: Direction;
        }) => {
            try {
                return await provider.execute(account, contract_name, "move", [
                    direction,
                ]);
            } catch (error) {
                console.error("Error executing move:", error);
                throw error;
            }
        };
        return { spawn, move };
    }
    return {
        actions: actions(),
    };
}

@rsodre
Copy link
Contributor

rsodre commented Jan 8, 2024

Looks clean!

I frequently wonder if I should return anything from system calls when I need to do something when it is finished.
A simple boolean for success/failure, or the tx that I can listen to somewhere else.
(btw, I still don't know how to do that with Dojo, or how to listen for events...)

What I end up doing is returning nothing, and listening to any component that should be updated after the system call

@gabe-ashe gabe-ashe moved this to 🆕 Prioritized in Dojo Jan 8, 2024
@gabe-ashe gabe-ashe moved this from 🆕 Prioritized to 🏗 In progress in Dojo Jan 8, 2024
@ponderingdemocritus ponderingdemocritus self-assigned this Jan 16, 2024
@ponderingdemocritus
Copy link
Contributor Author

We are adding Katana long polling into this, which will allow fo this. What we really need is to know if it hits Torii and then reflect in the client.

@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in Dojo Feb 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
No open projects
Status: Done
Development

No branches or pull requests

5 participants