Roadmap
Avançado

Text-based MMORPG com JavaScript

Entenda o processo de criação de um jogo MMORPG web com JavaScript

react
nextjs
javascript
prisma
trpc
tailwind

Criando rotas

O nextjs por padrão aceita que criemos a API usando a pasta server, nela podemos colocar nossas rotas e o nextjs irá entender que é uma rota da API.

Caso ainda tenha algum arquivo no caminho src/server/api/routers você pode remover, pois não iremos usar, no meu caso eu removi o arquivo post.ts.

O primeiro que vamos criar vai ser o nosso controlador de personagem, para isso vamos criar o arquivo character.ts na pasta src/server/api/routers.

Nossa estrutura base para praticamente todos os arquivos vai ser essa:

import { z } from "zod";

import {
  createTRPCRouter,
  protectedProcedure,
  publicProcedure,
} from "~/server/api/trpc";

export const postRouter = createTRPCRouter({
  // aqui vem todas as rotas que vamos interagir com o front-end via TRPC
});
Clique aqui para expandir

Criando um personagem

A primeira funcão que vamos criar vai ser a de criar um personagem, então vamos criar uma mutation para isso.

Caso não esteja familiarizado com TRPC existem 2 tipos funções que podemos criar, as mutations e as queries, as mutations são funções que alteram o estado do servidor, como por exemplo criar um personagem, já as queries são funções que retornam dados, como por exemplo pegar todos os personagens.

O formato de uma query consiste em:

nome: publicProcedure ou protectedProcedure
    .input(z.object({ input: z.string() })) <- Argumentos ou input da função
    .query(({ input }) => { // aqui vem a função que vai ser executada
      return {
        // aqui vem o retorno da query
      };
    }),
Clique aqui para expandir

PublicProcedure é uma função que pode ser acessada por qualquer um, já a protectedProcedure é uma função que só pode ser acessada por usuários logados.

Eu estou usando o Zod para validar os inputs mas você pode receber os inputs como um objeto e validar manualmente, mas o Zod é muito bom para isso.

Mole né?

Vamos criar agora as funções necessarias para manipular os personagens.

TRPC Router

GetChars

Nossa função getChars vai ser uma query, vamos retornar todos os personagens do usuário logado, vamos ordenar por level.

Create

Nossa funcão create vai ser uma mutation, vamos receber o nome e o Id da classe do personagem, vamos checar também se o usuário já tem 4 personagens, caso tenha vamos retornar um erro.

LevelUp

Nossa função levelUp vai ser uma mutation, vamos receber o id do personagem, vamos checar se o personagem existe e se ele pertence ao usuário logado, caso não exista ou não pertença ao usuário vamos retornar um erro, caso exista vamos aumentar o level do personagem em 1 e adicionar 5 pontos para ele distribuir.

Delete

Nossa função delete vai ser uma mutation, vamos receber o id do personagem, vamos checar se o personagem existe e se ele pertence ao usuário logado, caso não exista ou não pertença ao usuário vamos retornar um erro, caso exista vamos deletar o personagem.

character.ts
import { z } from "zod";

import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";

export const characterRouter = createTRPCRouter({
  getChars: protectedProcedure.query(async ({ ctx }) => {
    const chars = await ctx.db.character.findMany({
      where: {
        userId: ctx.session.user.id,
      },
      include: {
        class: true,
      },
    });

    return chars.sort((a, b) => a.level - b.level);
  }),
  create: protectedProcedure
    .input(z.object({ name: z.string().min(1).max(20), classId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const userChars = await ctx.db.character.findMany({
        where: {
          userId: ctx.session.user.id,
        },
      });

      if (userChars.length >= 4) {
        throw new Error("User has reached the maximum number of characters.");
      }

      return ctx.db.character.create({
        data: {
          name: input.name,
          class: {
            connect: {
              id: input.classId,
            },
          },
          user: {
            connect: {
              id: ctx.session.user.id,
            },
          },
          charStats: {
            create: {},
          },
          inventory: {
            create: {},
          },
        },
      });
    }),
  levelUp: protectedProcedure
    .input(z.object({ id: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const char = await ctx.db.character.findFirst({
        where: {
          id: input.id,
          userId: ctx.session.user.id,
        },
      });

      if (!char) {
        throw new Error("Character not found.");
      }

      return ctx.db.character.update({
        where: {
          id: input.id,
        },
        data: {
          level: char.level + 1,
          experience: 0,
          points: (char.points || 0) + 5,
        },
      });
    }),
  delete: protectedProcedure
    .input(z.object({ id: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const char = await ctx.db.character.findFirst({
        where: {
          id: input.id,
          userId: ctx.session.user.id,
        },
      });

      if (!char) {
        throw new Error("Character not found.");
      }

      return ctx.db.character.delete({
        where: {
          id: input.id,
        },
      });
    }),
});
Clique aqui para expandir

TRPC Provider

Para podermos acessar as funções do TRPC precisamos expor o arquivo que criamos dentro do router raiz, src/server/api/root.ts.

Para isso basta importar o arquivo que criamos e adicionar ele no objeto de routers.

root.ts
import { createTRPCRouter } from "~/server/api/trpc";
import { characterRouter } from "./routers/character";

/**
 * This is the primary router for your server.
 *
 * All routers added in /api/routers should be manually added here.
 */
export const appRouter = createTRPCRouter({
  character: characterRouter,
});

// export type definition of API
export type AppRouter = typeof appRouter;
Clique aqui para expandir
Opa, calma aí!

Parece que você não está logado, caso tenha interesse em salvar seu progresso de estudo faça login agora.