[Linux-only] basically bloxstap for sober

no more

-277
-111
.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
··· 1 - --- 2 - description: Use Bun instead of Node.js, npm, pnpm, or vite. 3 - globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json" 4 - alwaysApply: false 5 - --- 6 - 7 - Default to using Bun instead of Node.js. 8 - 9 - - Use `bun <file>` instead of `node <file>` or `ts-node <file>` 10 - - Use `bun test` instead of `jest` or `vitest` 11 - - Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild` 12 - - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` 13 - - Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>` 14 - - Bun automatically loads .env, so don't use dotenv. 15 - 16 - ## APIs 17 - 18 - - `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`. 19 - - `bun:sqlite` for SQLite. Don't use `better-sqlite3`. 20 - - `Bun.redis` for Redis. Don't use `ioredis`. 21 - - `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`. 22 - - `WebSocket` is built-in. Don't use `ws`. 23 - - Prefer `Bun.file` over `node:fs`'s readFile/writeFile 24 - - Bun.$`ls` instead of execa. 25 - 26 - ## Testing 27 - 28 - Use `bun test` to run tests. 29 - 30 - ```ts#index.test.ts 31 - import { test, expect } from "bun:test"; 32 - 33 - test("hello world", () => { 34 - expect(1).toBe(1); 35 - }); 36 - ``` 37 - 38 - ## Frontend 39 - 40 - Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind. 41 - 42 - Server: 43 - 44 - ```ts#index.ts 45 - import index from "./index.html" 46 - 47 - Bun.serve({ 48 - routes: { 49 - "/": index, 50 - "/api/users/:id": { 51 - GET: (req) => { 52 - return new Response(JSON.stringify({ id: req.params.id })); 53 - }, 54 - }, 55 - }, 56 - // optional websocket support 57 - websocket: { 58 - open: (ws) => { 59 - ws.send("Hello, world!"); 60 - }, 61 - message: (ws, message) => { 62 - ws.send(message); 63 - }, 64 - close: (ws) => { 65 - // handle close 66 - } 67 - }, 68 - development: { 69 - hmr: true, 70 - console: true, 71 - } 72 - }) 73 - ``` 74 - 75 - 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. 76 - 77 - ```html#index.html 78 - <html> 79 - <body> 80 - <h1>Hello, world!</h1> 81 - <script type="module" src="./frontend.tsx"></script> 82 - </body> 83 - </html> 84 - ``` 85 - 86 - With the following `frontend.tsx`: 87 - 88 - ```tsx#frontend.tsx 89 - import React from "react"; 90 - 91 - // import .css files directly and it works 92 - import './index.css'; 93 - 94 - import { createRoot } from "react-dom/client"; 95 - 96 - const root = createRoot(document.body); 97 - 98 - export default function Frontend() { 99 - return <h1>Hello, world!</h1>; 100 - } 101 - 102 - root.render(<Frontend />); 103 - ``` 104 - 105 - Then, run index.ts 106 - 107 - ```sh 108 - bun --hot ./index.ts 109 - ``` 110 - 111 - For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
-166
.cursor/rules/use-currentstate-api.mdc
··· 1 - --- 2 - description: Use CurrentState API to get current game details 3 - globs: "*.ts, *.js" 4 - alwaysApply: false 5 - --- 6 - 7 - # CurrentState API 8 - 9 - The CurrentState API allows plugins to determine what game is currently running and track game state changes in real-time. 10 - 11 - ## Overview 12 - 13 - The CurrentState API provides plugins with access to the current game state, including: 14 - - Place ID and Job ID of the current game 15 - - Server information (IP address, server type) 16 - - Player list and join/leave events 17 - - Real-time state change notifications 18 - 19 - ## Usage 20 - 21 - ### Basic Usage 22 - 23 - ```typescript 24 - import { Plugin } from "../api/Plugin"; 25 - 26 - const myPlugin = new Plugin({ 27 - name: "My Plugin", 28 - id: "my-plugin" 29 - }); 30 - 31 - // Get current state 32 - const currentState = myPlugin.currentState.getCurrentState(); 33 - console.log("Current place ID:", currentState.placeId); 34 - 35 - // Check if in game 36 - if (myPlugin.currentState.isInGame()) { 37 - console.log("Currently in game:", myPlugin.currentState.getPlaceId()); 38 - } 39 - ``` 40 - 41 - ### State Change Notifications 42 - 43 - ```typescript 44 - // Subscribe to state changes 45 - const unsubscribe = myPlugin.currentState.onStateChange((state) => { 46 - if (state.isInGame) { 47 - console.log(`Joined game: ${state.placeId}`); 48 - console.log(`Players: ${state.players.length}`); 49 - } else { 50 - console.log("Left game"); 51 - } 52 - }); 53 - 54 - // Don't forget to unsubscribe when done 55 - unsubscribe(); 56 - ``` 57 - 58 - ### Hooking into Events 59 - 60 - ```typescript 61 - // Hook into game join/leave events 62 - myPlugin.hookLog("GAME_JOIN", (gameData) => { 63 - console.log("Joined game:", gameData.placeId); 64 - }); 65 - 66 - myPlugin.hookLog("GAME_LEAVE", () => { 67 - console.log("Left game"); 68 - }); 69 - 70 - // Hook into player join/leave events 71 - myPlugin.hookLog("JOIN_LEAVE", (playerAction) => { 72 - console.log(`${playerAction.name} ${playerAction.action.toLowerCase()}ed`); 73 - }); 74 - ``` 75 - 76 - ## API Reference 77 - 78 - ### CurrentStateAPI Interface 79 - 80 - ```typescript 81 - interface CurrentStateAPI { 82 - /** Get the current game state */ 83 - getCurrentState(): GameState; 84 - 85 - /** Subscribe to state changes */ 86 - onStateChange(callback: (state: GameState) => void): () => void; 87 - 88 - /** Check if currently in a game */ 89 - isInGame(): boolean; 90 - 91 - /** Get the current place ID */ 92 - getPlaceId(): string | null; 93 - 94 - /** Get the current job ID */ 95 - getJobId(): string | null; 96 - } 97 - ``` 98 - 99 - ### GameState Interface 100 - 101 - ```typescript 102 - interface GameState { 103 - /** The Roblox Place ID of the current game */ 104 - placeId: string; 105 - 106 - /** The Job ID of the current server instance */ 107 - jobId: string; 108 - 109 - /** The IP address of the server */ 110 - ipAddr: string; 111 - 112 - /** Roblox's proxy IP address (if available) */ 113 - ipAddrUdmux?: string; 114 - 115 - /** The type of server (public, private, reserved) */ 116 - serverType: ServerType; 117 - 118 - /** Whether the user is currently in a game */ 119 - isInGame: boolean; 120 - 121 - /** Timestamp when the game was joined */ 122 - joinTime?: number; 123 - 124 - /** List of players currently in the game */ 125 - players: PlayerInfo[]; 126 - } 127 - ``` 128 - 129 - ### PlayerInfo Interface 130 - 131 - ```typescript 132 - interface PlayerInfo { 133 - /** Player's display name */ 134 - name: string; 135 - 136 - /** Player's user ID */ 137 - id: string; 138 - 139 - /** When the player joined (timestamp) */ 140 - joinTime: number; 141 - } 142 - ``` 143 - 144 - ### ServerType Enum 145 - 146 - ```typescript 147 - enum ServerType { 148 - PUBLIC = "PUBLIC", 149 - PRIVATE = "PRIVATE", 150 - RESERVED = "RESERVED" 151 - } 152 - ``` 153 - 154 - ## Best Practices 155 - 156 - 1. **Always unsubscribe**: When subscribing to state changes, make sure to call the unsubscribe function to prevent memory leaks. 157 - 158 - 2. **Check state before accessing**: Always check `isInGame()` before accessing game-specific properties like `placeId` or `jobId`. 159 - 160 - 3. **Handle errors gracefully**: State change callbacks should handle errors to prevent crashes. 161 - 162 - 4. **Use appropriate hooks**: Use the appropriate log hooks (`GAME_JOIN`, `GAME_LEAVE`, `JOIN_LEAVE`) for specific event handling. 163 - 164 - ## Example Plugin 165 - 166 - See `src/plugins/currentStateExample.ts` for a complete example of how to use the CurrentState API in a plugin.