Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 3 additions & 26 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,27 +1,4 @@
# Dependencies
node_modules/

# Build output
dist/

# Environment files
.env
.env.local
.env.*.local

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS files
node_modules
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*

# Lock files (optional - remove if you want to track)
# package-lock.json
dist
.env
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "es5",
"quoteProps": "preserve",
"plugins": []
}
52 changes: 52 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
You are writing a Devvit web application that will be executed on Reddit.com.

## Tech Stack

- **Frontend**: GameMaker, Vite
- **Backend**: Node.js v22 serverless environment (Devvit), Hono, TRPC
- **Communication**: tRPC v11 for end-to-end type safety

## Layout & Architecture

- `/src/server`: **Backend Code**. This runs in a secure, serverless environment.
- `trpc.ts`: Defines the API router and procedures.
- `index.ts`: Main server entry point (Hono app).
- Access `redis`, `reddit`, and `context` here via `@devvit/web/server`.
- `/src/client`: **Frontend Code**. This is executed inside of an iFrame on reddit.com
- To add an entrypoint, create a HTML file and add to the mapping inside of `devvit.json`
- Entrypoints:
- `game.html`: The main React entry point (Expanded View).
- `splash.html`: The initial React entry point (Inline View). This will be shown in the reddit.com feed. Please keep it fast and keep heavy dependencies inside of `game.html`
- `/src/shared`: **Shared Code**. Code to share between the client and server

## Frontend

### Rules

- Instead of `window.location` or `window.assign`, use `navigateTo` from `@devvit/web/client`

### Limitations

- `window.alert`: Use `showToast` or `showForm` from `@devvit/web/client`
- File downloads: Use clipboard API with `showToast` to confirm
- Geolocation, camera, microphone, and notifications web APIs: No alternatives
- Inline script tags inside of `html` files: Use a script tag and separate js/ts file

## Commands

- `npm run type-check`: Check typescript types
- `npm run lint`: Check the linter
- `npm run test -- my-file-name`: Run tests isolated to a file

## Code Style

- Prefer type aliases over interfaces when writing typescript
- Prefer named exports over default exports
- Never cast typescript types

## Global Rules

- You may find code that references blocks or `@devvit/public-api` while building a feature. Do NOT use this code as this project is configured to use Devvit web only.
- Whenever you add an endpoint for a new menu item action, ensure that you've added the corresponding mapping to `devvit.json` so that it is properly registered

Docs: https://developers.reddit.com/docs/llms.txt.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ This is intended as a GameMaker wasm equivalent to the Devvit templates and is b

## Get Started

For instructions on how to get setup as a Reddit developer and start deploying GameMaker games to reddit, see [How To Build](docs/HowToBuild.md)
For instructions on how to get setup as a Reddit developer and start deploying GameMaker games to reddit, see [How To Build](docs/HowToBuild.md)
16 changes: 15 additions & 1 deletion devvit.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,24 @@
"location": "subreddit",
"forUserType": "moderator",
"endpoint": "/internal/menu/post-create"
},
{
"label": "Example form",
"description": "Show a simple form",
"location": "subreddit",
"forUserType": "moderator",
"endpoint": "/internal/menu/example-form"
}
]
},
"forms": {
"exampleForm": "/internal/form/example-submit"
},
"triggers": {
"onAppInstall": "/internal/on-app-install"
"onAppInstall": "/internal/triggers/on-app-install"
},
"scripts": {
"build": "vite build",
"dev": "vite build --watch"
}
}
23 changes: 20 additions & 3 deletions docs/HowToBuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Before you begin, ensure you have the following installed and configured:

- **Node.js 22+** - Download from [nodejs.org](https://nodejs.org/)
- **GameMaker** - Any recent 2024.1400.3 Beta release (or newer), which you can download from [the release notes site](https://releases.gamemaker.io/) and must have also configured already for GX.games YYC development by following [its setup guide](https://github.com/YoYoGames/GameMaker-Bugs/wiki#platform-setup-guides)
- You may use older versions of GameMaker but will need to use the [GMEXT-Reddit Extension](https://github.com/YoYoGames/GMEXT-Reddit/) which includes the example project and requires you to build your game with the gx.games target rather than the reddit target. Further details can be found in the extension repository.
- You may use older versions of GameMaker but will need to use the [GMEXT-Reddit Extension](https://github.com/YoYoGames/GMEXT-Reddit/) which includes the example project and requires you to build your game with the gx.games target rather than the reddit target. Further details can be found in the extension repository.
- **Reddit Developer Account** - Sign up at [developers.reddit.com](https://developers.reddit.com/)

---
Expand Down Expand Up @@ -72,13 +72,15 @@ Open the GameMaker project that you want to deploy to Reddit.
In the **Devvit Project ID** field, enter the short, no-spaces game name that you registered with Reddit earlier

**Example:**

```
my-game
```

In the **Devvit Project Path** field, enter the full path to your Devvit project directory that you created in the previous section.

**Example:**

```
C:\Users\YourName\Projects\my-game
```
Expand All @@ -90,6 +92,7 @@ Click **OK** or **Apply** to save your settings.
#### Step 4: Verify GameMaker Export Settings

Ensure your project is configured to export to WebAssembly:

- **Target Platform**: Reddit
- **Output Format**: GMS2 VM

Expand All @@ -112,6 +115,7 @@ npm run dev
**You only need to do this once per GameMaker session** - leave the terminal app running throughout your development session and then close it whenever you're done with all your Reddit builds for the day.

This command does several things:

- Starts a local development server
- Uploads your app to Reddit's Devvit platform
- Provides a link to test your app on Reddit (not clickable - you will need to copy/paste it manually the first time and thereafter can just refresh your browser tab
Expand Down Expand Up @@ -168,13 +172,15 @@ When you're ready to test your changes:
> If not running on Windows, ensure that execute permissions are enabled on the `setup-gamemaker-devvit.sh` script within your devvit project

GameMaker will:

- Build your game to WebAssembly
- Automatically copy the necessary files to your Devvit project directory (specified in Game Options)
- The build output goes to `src/client/public/` in your Devvit project

#### Step 3: Wait for Devvit to Detect Changes

The `npm run dev` process (still running in your terminal) will shortly thereafter:

- Automatically detect the new/changed files
- Re-upload your game to Reddit's platform
- Display upload progress in the terminal
Expand All @@ -184,6 +190,7 @@ Wait for the update to complete. Devvit will provide a link to the updated commu
#### Step 4: Test on Reddit

Once the upload completes:

1. Open the link provided by `npm run dev` in your browser (or refresh if already open)
2. Test your game directly on Reddit
3. Iterate!
Expand Down Expand Up @@ -252,18 +259,22 @@ your-project/
## Adding Game Features

### Backend APIs

Add game-specific endpoints in `src/server/index.ts`:

```typescript
// Example: Save player score
router.post("/api/save-score", async (req, res) => {
router.post('/api/save-score', async (req, res) => {
const { score } = req.body;
// Save to Redis, database, etc.
res.json({ success: true });
});
```

### Type Definitions

Add API types in `src/shared/types/api.ts`:

```typescript
export type SaveScoreRequest = {
score: number;
Expand All @@ -278,6 +289,7 @@ export type SaveScoreRequest = {
### `npm run dev` doesn't detect changes

**Solution:**

- Verify the Devvit Project Path in GameMaker is correct
- Ensure files are being copied to the correct directory (`src/client/public/`)
- Check terminal for any error messages
Expand All @@ -286,13 +298,15 @@ export type SaveScoreRequest = {
### GameMaker build fails

**Solution:**

- Check that the Reddit platform is properly configured
- Ensure the Devvit Project Path exists and is writable
- As a sanity-check, see if you can successfully build packages for the GX.games target inside GameMaker, rather than the Reddit one. If you can, check your OS file permissions/antivirus client are not blocking your Reddit-specific folders.

### Game doesn't appear on Reddit

**Solution:**

- Confirm `npm run dev` completed the upload successfully
- Check the terminal for any error messages
- Try a hard refresh in your browser (Ctrl+Shift+R or Cmd+Shift+R)
Expand All @@ -301,22 +315,25 @@ export type SaveScoreRequest = {
### Files are in the wrong location

**Solution:**

- GameMaker should copy files to `src/client/public/` in your Devvit project
- Verify the Devvit Project Path setting in GameMaker Game Options
- Check your OS file permissions/antivirus client are not blocking your Reddit-specific folders.

### Node.js version issues

**Solution:**

- Devvit requires Node.js 22+
- Run `node --version` to check your version
- Update Node.js if necessary: [Download latest version](https://nodejs.org/)

### setup-gamemaker-devvit.sh Permissions Issue

**Solution**

- Ensure that execute permissions are enabled on the `setup-gamemaker-devvit.sh` script
- Run `ls -l setup-gamemaker-devvit.sh` to verify permissions
- Run `ls -l setup-gamemaker-devvit.sh` to verify permissions
- Run `chmod +x setup-gamemaker-devvit.sh` to grant execute permissions

---
Expand Down
68 changes: 68 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import js from '@eslint/js';
import tseslint from 'typescript-eslint';

export default defineConfig([
tseslint.configs.recommended,
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['src/server/**/*.{ts,tsx,mjs,cjs,js}'],
languageOptions: {
ecmaVersion: 2023,
globals: globals.node,
parserOptions: {
project: ['./tools/tsconfig.server.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['src/shared/**/*.{ts,tsx,mjs,cjs,js}'],
languageOptions: {
ecmaVersion: 2023,
globals: globals.browser,
parserOptions: {
project: ['./tools/tsconfig.shared.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['src/client/**/*.{ts,tsx}'],
ignores: ['src/server/**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2023,
globals: globals.browser,
parserOptions: {
project: ['./tools/tsconfig.client.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
{
files: ['**/*.{js,mjs,cjs,ts,tsx}'],
rules: {
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-unused-vars': ['off'],
'no-unused-vars': ['off'],
},
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'eslint.config.js',
'**/vite.config.ts',
'devvit.config.ts',
],
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
},
},
plugins: { js },
extends: ['js/recommended'],
},
]);
43 changes: 24 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,34 @@
"license": "BSD-3-Clause",
"type": "module",
"scripts": {
"postinstall": "npm run build",
"build:client": "cd src/client && vite build",
"build:server": "cd src/server && vite build",
"build": "npm run build:client && npm run build:server",
"deploy": "npm run build && devvit upload",
"dev": "concurrently -k -p \"[{name}]\" -n \"CLIENT,SERVER,DEVVIT\" -c \"blue,green,magenta\" \"npm run dev:client\" \"npm run dev:server\" \"npm run dev:devvit\"",
"dev:client": "cd src/client && vite build --watch",
"dev:devvit": "dotenv -e .env -- devvit playtest",
"dev:server": "cd src/server && vite build --watch",
"build": "vite build",
"deploy": "npm run type-check && npm run lint && npm run test && devvit upload",
"dev": "devvit playtest",
"launch": "npm run deploy && devvit publish",
"lint": "eslint 'src/**/*.{ts,tsx}'",
"login": "devvit login",
"launch": "npm run build && npm run deploy && devvit publish",
"prettier": "prettier --write .",
"test": "vitest run",
"type-check": "tsc --build"
},
"engines": {
"node": ">=22.12.0"
},
"dependencies": {
"@devvit/web": "0.12.8",
"devvit": "0.12.8",
"express": "5.1.0"
"@devvit/start": "0.12.11-next-2026-01-30-17-09-49-e9c512a0d.0",
"@devvit/web": "0.12.11-next-2026-01-30-17-09-49-e9c512a0d.0",
"@hono/node-server": "^1.19.9",
"devvit": "0.12.11-next-2026-01-30-17-09-49-e9c512a0d.0",
"hono": "4.11.7"
},
"devDependencies": {
"@types/express": "5.0.1",
"concurrently": "9.1.2",
"dotenv-cli": "8.0.0",
"typescript": "5.8.2",
"vite": "6.2.4"
"@eslint/js": "9.39.2",
"@types/node": "^22.19.7",
"eslint": "9.39.2",
"globals": "17.2.0",
"prettier": "3.8.1",
"typescript": "5.9.3",
"typescript-eslint": "8.54.0",
"vite": "7.3.1"
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
Loading