···11----
22-description: Use Bun instead of Node.js, npm, pnpm, or vite.
33-globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
44-alwaysApply: false
55----
66-77-Default to using Bun instead of Node.js.
88-99-- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
1010-- Use `bun test` instead of `jest` or `vitest`
1111-- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
1212-- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
1313-- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
1414-- Bun automatically loads .env, so don't use dotenv.
1515-1616-## APIs
1717-1818-- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
1919-- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
2020-- `Bun.redis` for Redis. Don't use `ioredis`.
2121-- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
2222-- `WebSocket` is built-in. Don't use `ws`.
2323-- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
2424-- Bun.$`ls` instead of execa.
2525-2626-## Testing
2727-2828-Use `bun test` to run tests.
2929-3030-```ts#index.test.ts
3131-import { test, expect } from "bun:test";
3232-3333-test("hello world", () => {
3434- expect(1).toBe(1);
3535-});
3636-```
3737-3838-## Frontend
3939-4040-Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
4141-4242-Server:
4343-4444-```ts#index.ts
4545-import index from "./index.html"
4646-4747-Bun.serve({
4848- routes: {
4949- "/": index,
5050- "/api/users/:id": {
5151- GET: (req) => {
5252- return new Response(JSON.stringify({ id: req.params.id }));
5353- },
5454- },
5555- },
5656- // optional websocket support
5757- websocket: {
5858- open: (ws) => {
5959- ws.send("Hello, world!");
6060- },
6161- message: (ws, message) => {
6262- ws.send(message);
6363- },
6464- close: (ws) => {
6565- // handle close
6666- }
6767- },
6868- development: {
6969- hmr: true,
7070- console: true,
7171- }
7272-})
7373-```
7474-7575-HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
7676-7777-```html#index.html
7878-<html>
7979- <body>
8080- <h1>Hello, world!</h1>
8181- <script type="module" src="./frontend.tsx"></script>
8282- </body>
8383-</html>
8484-```
8585-8686-With the following `frontend.tsx`:
8787-8888-```tsx#frontend.tsx
8989-import React from "react";
9090-9191-// import .css files directly and it works
9292-import './index.css';
9393-9494-import { createRoot } from "react-dom/client";
9595-9696-const root = createRoot(document.body);
9797-9898-export default function Frontend() {
9999- return <h1>Hello, world!</h1>;
100100-}
101101-102102-root.render(<Frontend />);
103103-```
104104-105105-Then, run index.ts
106106-107107-```sh
108108-bun --hot ./index.ts
109109-```
110110-111111-For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
-166
.cursor/rules/use-currentstate-api.mdc
···11----
22-description: Use CurrentState API to get current game details
33-globs: "*.ts, *.js"
44-alwaysApply: false
55----
66-77-# CurrentState API
88-99-The CurrentState API allows plugins to determine what game is currently running and track game state changes in real-time.
1010-1111-## Overview
1212-1313-The CurrentState API provides plugins with access to the current game state, including:
1414-- Place ID and Job ID of the current game
1515-- Server information (IP address, server type)
1616-- Player list and join/leave events
1717-- Real-time state change notifications
1818-1919-## Usage
2020-2121-### Basic Usage
2222-2323-```typescript
2424-import { Plugin } from "../api/Plugin";
2525-2626-const myPlugin = new Plugin({
2727- name: "My Plugin",
2828- id: "my-plugin"
2929-});
3030-3131-// Get current state
3232-const currentState = myPlugin.currentState.getCurrentState();
3333-console.log("Current place ID:", currentState.placeId);
3434-3535-// Check if in game
3636-if (myPlugin.currentState.isInGame()) {
3737- console.log("Currently in game:", myPlugin.currentState.getPlaceId());
3838-}
3939-```
4040-4141-### State Change Notifications
4242-4343-```typescript
4444-// Subscribe to state changes
4545-const unsubscribe = myPlugin.currentState.onStateChange((state) => {
4646- if (state.isInGame) {
4747- console.log(`Joined game: ${state.placeId}`);
4848- console.log(`Players: ${state.players.length}`);
4949- } else {
5050- console.log("Left game");
5151- }
5252-});
5353-5454-// Don't forget to unsubscribe when done
5555-unsubscribe();
5656-```
5757-5858-### Hooking into Events
5959-6060-```typescript
6161-// Hook into game join/leave events
6262-myPlugin.hookLog("GAME_JOIN", (gameData) => {
6363- console.log("Joined game:", gameData.placeId);
6464-});
6565-6666-myPlugin.hookLog("GAME_LEAVE", () => {
6767- console.log("Left game");
6868-});
6969-7070-// Hook into player join/leave events
7171-myPlugin.hookLog("JOIN_LEAVE", (playerAction) => {
7272- console.log(`${playerAction.name} ${playerAction.action.toLowerCase()}ed`);
7373-});
7474-```
7575-7676-## API Reference
7777-7878-### CurrentStateAPI Interface
7979-8080-```typescript
8181-interface CurrentStateAPI {
8282- /** Get the current game state */
8383- getCurrentState(): GameState;
8484-8585- /** Subscribe to state changes */
8686- onStateChange(callback: (state: GameState) => void): () => void;
8787-8888- /** Check if currently in a game */
8989- isInGame(): boolean;
9090-9191- /** Get the current place ID */
9292- getPlaceId(): string | null;
9393-9494- /** Get the current job ID */
9595- getJobId(): string | null;
9696-}
9797-```
9898-9999-### GameState Interface
100100-101101-```typescript
102102-interface GameState {
103103- /** The Roblox Place ID of the current game */
104104- placeId: string;
105105-106106- /** The Job ID of the current server instance */
107107- jobId: string;
108108-109109- /** The IP address of the server */
110110- ipAddr: string;
111111-112112- /** Roblox's proxy IP address (if available) */
113113- ipAddrUdmux?: string;
114114-115115- /** The type of server (public, private, reserved) */
116116- serverType: ServerType;
117117-118118- /** Whether the user is currently in a game */
119119- isInGame: boolean;
120120-121121- /** Timestamp when the game was joined */
122122- joinTime?: number;
123123-124124- /** List of players currently in the game */
125125- players: PlayerInfo[];
126126-}
127127-```
128128-129129-### PlayerInfo Interface
130130-131131-```typescript
132132-interface PlayerInfo {
133133- /** Player's display name */
134134- name: string;
135135-136136- /** Player's user ID */
137137- id: string;
138138-139139- /** When the player joined (timestamp) */
140140- joinTime: number;
141141-}
142142-```
143143-144144-### ServerType Enum
145145-146146-```typescript
147147-enum ServerType {
148148- PUBLIC = "PUBLIC",
149149- PRIVATE = "PRIVATE",
150150- RESERVED = "RESERVED"
151151-}
152152-```
153153-154154-## Best Practices
155155-156156-1. **Always unsubscribe**: When subscribing to state changes, make sure to call the unsubscribe function to prevent memory leaks.
157157-158158-2. **Check state before accessing**: Always check `isInGame()` before accessing game-specific properties like `placeId` or `jobId`.
159159-160160-3. **Handle errors gracefully**: State change callbacks should handle errors to prevent crashes.
161161-162162-4. **Use appropriate hooks**: Use the appropriate log hooks (`GAME_JOIN`, `GAME_LEAVE`, `JOIN_LEAVE`) for specific event handling.
163163-164164-## Example Plugin
165165-166166-See `src/plugins/currentStateExample.ts` for a complete example of how to use the CurrentState API in a plugin.