roport 1.0.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/bin/roport.js +2 -1
- package/package.json +2 -2
- package/src/server.js +171 -10
- package/templates/default/DOCUMENTATION.md +309 -0
- package/templates/default/README.md +35 -0
- package/templates/default/RoportSyncPlugin.rbxmx +1315 -0
- package/templates/default/default.project.json +6 -0
- package/templates/default/src/Roport/Modules/Sync.lua +10 -6
- package/templates/default/src/Roport/init.server.lua +66 -0
- package/templates/default/src/lighting/Atmosphere/init.meta.json +1 -1
- package/templates/default/src/lighting/Bloom/init.meta.json +1 -1
- package/templates/default/src/lighting/DepthOfField/init.meta.json +1 -1
- package/templates/default/src/lighting/Sky/init.meta.json +1 -1
- package/templates/default/src/lighting/SunRays/init.meta.json +1 -1
- package/templates/default/src/server/network/CombatHandler.server.luau +2 -1
- package/templates/default/src/server/network/ShopHandler.server.luau +6 -5
- package/templates/default/src/server/world/WorldLoader.server.luau +7 -9
- package/templates/default/src/shared/Prefabs/ExampleTree/Part/init.meta.json +10 -0
- package/templates/default/src/shared/Prefabs/ExampleTree/init.meta.json +6 -0
- package/templates/default/src/shared/Prefabs/init.meta.json +6 -0
- package/templates/default/src/shared/movement/Pathfind.luau +42 -0
- package/templates/default/src/shared/network/Network.luau +1 -1
- package/templates/default/src/workspace/Accessory Storage/init.meta.json +1 -1
- package/templates/default/src/workspace/Baseplate/Texture/init.meta.json +1 -1
- package/templates/default/src/workspace/Baseplate/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/Model/FirstFloor/Model/Smooth Block Model/Snap/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/Model/FirstFloor/Model/Smooth Block Model/Weld/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/Model/FirstFloor/Model/Smooth Block Model/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/Model/FirstFloor/Model/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/Model/FirstFloor/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/Model/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/Decorated old roblox house/init.meta.json +1 -1
- package/templates/default/src/workspace/Map/init.meta.json +1 -1
- package/templates/default/src/workspace/Part2/Decal/init.meta.json +1 -1
- package/templates/default/src/workspace/Part2/init.meta.json +1 -1
- package/templates/default/src/workspace/SpawnLocation/Decal/init.meta.json +1 -1
- package/templates/default/src/workspace/SpawnLocation/init.meta.json +1 -1
- package/templates/default/AI_INSTRUCTIONS.md +0 -72
package/README.md
CHANGED
|
@@ -45,3 +45,19 @@ This will start a local server on port `3456` (default).
|
|
|
45
45
|
3. Start the server with `roport serve`.
|
|
46
46
|
4. Install the **Roport** plugin in Roblox Studio.
|
|
47
47
|
5. Connect via the plugin in Roblox Studio.
|
|
48
|
+
|
|
49
|
+
## Diagnostics
|
|
50
|
+
|
|
51
|
+
When the Roport plugin loads in Roblox Studio, it will automatically run a diagnostic check and output the results to the Output window. This check verifies:
|
|
52
|
+
- Connection to the Roport server.
|
|
53
|
+
- Correct mapping of project folders (`Shared`, `Server`, `Client`).
|
|
54
|
+
|
|
55
|
+
Look for the `[Roport]` prefix in the console to see the status.
|
|
56
|
+
|
|
57
|
+
## Testing
|
|
58
|
+
|
|
59
|
+
To run the integration test suite for the CLI:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
node test/test_suite.js
|
|
63
|
+
```
|
package/bin/roport.js
CHANGED
|
@@ -33,7 +33,8 @@ program
|
|
|
33
33
|
await fs.copy(templateDir, targetDir);
|
|
34
34
|
console.log(chalk.green('Success! Project initialized.'));
|
|
35
35
|
console.log(chalk.white('Run `roport serve` to start the sync server.'));
|
|
36
|
-
console.log(chalk.white('Check
|
|
36
|
+
console.log(chalk.white('Check DOCUMENTATION.md for usage details.'));
|
|
37
|
+
console.log(chalk.white('Install RoportSyncPlugin.rbxmx in your Roblox Studio project.'));
|
|
37
38
|
} else {
|
|
38
39
|
console.error(chalk.red(`Error: Template directory not found at ${templateDir}`));
|
|
39
40
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roport",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A sync server for Roblox development. Works with the Roport Roblox Plugin.",
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "A sync server for Roblox development. Works with the Roport Roblox Plugin. Features AI integration and full project sync.",
|
|
5
5
|
"main": "src/server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"roport": "./bin/roport.js"
|
package/src/server.js
CHANGED
|
@@ -19,6 +19,7 @@ function startServer(port) {
|
|
|
19
19
|
// State for two-way sync
|
|
20
20
|
let changedFiles = new Set();
|
|
21
21
|
let commandQueue = []; // Queue for commands to be sent to Roblox
|
|
22
|
+
let executionResults = new Map(); // Store results of executed scripts
|
|
22
23
|
let isWriting = false; // Lock to prevent loops (Roblox -> File -> Watcher -> Roblox)
|
|
23
24
|
|
|
24
25
|
// Watch for file changes locally
|
|
@@ -26,11 +27,11 @@ function startServer(port) {
|
|
|
26
27
|
console.log(chalk.gray(`Starting file watcher on ${projectRoot}...`));
|
|
27
28
|
fs.watch(projectRoot, { recursive: true }, (eventType, filename) => {
|
|
28
29
|
if (filename && !isWriting) {
|
|
29
|
-
// Ignore system folders and the cli folder itself if we are watching root
|
|
30
|
-
if (filename.includes('node_modules') || filename.includes('.git') || filename.startsWith('cli')) return;
|
|
31
|
-
|
|
32
30
|
// Normalize path to forward slashes
|
|
33
31
|
const relativePath = filename.replace(/\\/g, '/');
|
|
32
|
+
|
|
33
|
+
// Check ignore rules
|
|
34
|
+
if (isIgnored(relativePath)) return;
|
|
34
35
|
|
|
35
36
|
// Debounce/Deduplicate slightly
|
|
36
37
|
if (!changedFiles.has(relativePath)) {
|
|
@@ -46,6 +47,125 @@ function startServer(port) {
|
|
|
46
47
|
// Increase limit for large file batches
|
|
47
48
|
app.use(express.json({ limit: '50mb' }));
|
|
48
49
|
|
|
50
|
+
// Ignore Logic
|
|
51
|
+
let ignoreRules = [];
|
|
52
|
+
|
|
53
|
+
function loadIgnoreRules() {
|
|
54
|
+
ignoreRules = [];
|
|
55
|
+
const ignoreFiles = ['.rojoignore', '.gitignore'];
|
|
56
|
+
|
|
57
|
+
for (const file of ignoreFiles) {
|
|
58
|
+
const ignorePath = path.join(projectRoot, file);
|
|
59
|
+
if (fs.existsSync(ignorePath)) {
|
|
60
|
+
try {
|
|
61
|
+
const content = fs.readFileSync(ignorePath, 'utf8');
|
|
62
|
+
const lines = content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
|
|
63
|
+
ignoreRules.push(...lines);
|
|
64
|
+
console.log(chalk.gray(`Loaded ${lines.length} ignore rules from ${file}`));
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.warn(chalk.yellow(`Failed to read ${file}:`), e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Always ignore internal folders
|
|
71
|
+
ignoreRules.push('.git', 'node_modules', 'cli');
|
|
72
|
+
}
|
|
73
|
+
loadIgnoreRules();
|
|
74
|
+
|
|
75
|
+
function isIgnored(filePath) {
|
|
76
|
+
// Normalize path
|
|
77
|
+
const relativePath = filePath.replace(/\\/g, '/');
|
|
78
|
+
|
|
79
|
+
for (const rule of ignoreRules) {
|
|
80
|
+
// Simple glob matching
|
|
81
|
+
// 1. Exact match
|
|
82
|
+
if (relativePath === rule) return true;
|
|
83
|
+
|
|
84
|
+
// 2. Directory match (rule ends with /)
|
|
85
|
+
if (rule.endsWith('/') && relativePath.startsWith(rule)) return true;
|
|
86
|
+
|
|
87
|
+
// 3. Extension match (*.lua)
|
|
88
|
+
if (rule.startsWith('*.') && relativePath.endsWith(rule.slice(1))) return true;
|
|
89
|
+
|
|
90
|
+
// 4. Folder match (node_modules) - check if it's a segment
|
|
91
|
+
if (!rule.includes('/') && !rule.includes('*')) {
|
|
92
|
+
const segments = relativePath.split('/');
|
|
93
|
+
if (segments.includes(rule)) return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 5. Basic wildcard (src/*)
|
|
97
|
+
if (rule.endsWith('/*')) {
|
|
98
|
+
const base = rule.slice(0, -2);
|
|
99
|
+
if (relativePath.startsWith(base + '/') && relativePath.split('/').length === base.split('/').length + 1) return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 6. Recursive wildcard (src/**)
|
|
103
|
+
if (rule.endsWith('/**')) {
|
|
104
|
+
const base = rule.slice(0, -3);
|
|
105
|
+
if (relativePath.startsWith(base + '/')) return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Project Configuration Parsing
|
|
112
|
+
function getProjectConfig() {
|
|
113
|
+
const configPath = path.join(projectRoot, 'default.project.json');
|
|
114
|
+
if (fs.existsSync(configPath)) {
|
|
115
|
+
try {
|
|
116
|
+
const config = fs.readJsonSync(configPath);
|
|
117
|
+
const mounts = [];
|
|
118
|
+
|
|
119
|
+
function traverse(node, currentPath) {
|
|
120
|
+
if (node.$path) {
|
|
121
|
+
mounts.push({
|
|
122
|
+
robloxPath: currentPath,
|
|
123
|
+
filePath: node.$path,
|
|
124
|
+
className: node.$className
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const key in node) {
|
|
129
|
+
if (!key.startsWith('$') && typeof node[key] === 'object') {
|
|
130
|
+
traverse(node[key], [...currentPath, key]);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (config.tree) {
|
|
136
|
+
traverse(config.tree, []);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { name: config.name, mounts };
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error(chalk.red("Failed to parse default.project.json:"), e);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
app.get('/config', (req, res) => {
|
|
148
|
+
const config = getProjectConfig();
|
|
149
|
+
if (config) {
|
|
150
|
+
res.send(config);
|
|
151
|
+
} else {
|
|
152
|
+
// Fallback for legacy projects without config
|
|
153
|
+
res.send({
|
|
154
|
+
mounts: [
|
|
155
|
+
{ robloxPath: ["ServerScriptService"], filePath: "src/server" },
|
|
156
|
+
{ robloxPath: ["ReplicatedStorage"], filePath: "src/shared" },
|
|
157
|
+
{ robloxPath: ["StarterPlayer", "StarterPlayerScripts"], filePath: "src/client" },
|
|
158
|
+
{ robloxPath: ["Workspace"], filePath: "src/workspace" },
|
|
159
|
+
{ robloxPath: ["StarterGui"], filePath: "src/interface" },
|
|
160
|
+
{ robloxPath: ["StarterPack"], filePath: "src/tools" },
|
|
161
|
+
{ robloxPath: ["Lighting"], filePath: "src/lighting" },
|
|
162
|
+
{ robloxPath: ["ReplicatedFirst"], filePath: "src/first" },
|
|
163
|
+
{ robloxPath: ["SoundService"], filePath: "src/sounds" }
|
|
164
|
+
]
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
49
169
|
app.get('/ping', (req, res) => {
|
|
50
170
|
res.send('pong');
|
|
51
171
|
});
|
|
@@ -100,17 +220,48 @@ function startServer(port) {
|
|
|
100
220
|
});
|
|
101
221
|
|
|
102
222
|
app.post('/log', (req, res) => {
|
|
103
|
-
const { message,
|
|
104
|
-
const timeStr = new Date(timestamp * 1000).toLocaleTimeString();
|
|
223
|
+
const { message, type, timestamp } = req.body;
|
|
224
|
+
const timeStr = new Date((timestamp || Date.now() / 1000) * 1000).toLocaleTimeString();
|
|
105
225
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
}
|
|
226
|
+
let color = chalk.white;
|
|
227
|
+
if (type === 2 || type === 'Error') color = chalk.red; // Error
|
|
228
|
+
else if (type === 1 || type === 'Warning') color = chalk.yellow; // Warning
|
|
229
|
+
|
|
230
|
+
console.log(chalk.gray(`[STUDIO ${timeStr}]`) + " " + color(message));
|
|
231
|
+
res.send({ success: true });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// AI Interface: Remote Execution
|
|
235
|
+
app.post('/execute', (req, res) => {
|
|
236
|
+
const { code } = req.body;
|
|
237
|
+
if (!code) return res.status(400).send('Missing code');
|
|
238
|
+
|
|
239
|
+
const id = Date.now().toString();
|
|
240
|
+
commandQueue.push({ type: 'EXECUTE', id, code });
|
|
241
|
+
console.log(chalk.blue(`Queued execution ${id}`));
|
|
242
|
+
|
|
243
|
+
// Wait for result (long polling implementation could be better, but simple polling works for now)
|
|
244
|
+
res.send({ id, status: 'queued' });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
app.post('/execute-result', (req, res) => {
|
|
248
|
+
const { id, success, result, error } = req.body;
|
|
249
|
+
executionResults.set(id, { success, result, error });
|
|
250
|
+
console.log(chalk.blue(`Received result for ${id}: ${success ? 'Success' : 'Failed'}`));
|
|
111
251
|
res.send({ success: true });
|
|
112
252
|
});
|
|
113
253
|
|
|
254
|
+
app.get('/execute/:id', (req, res) => {
|
|
255
|
+
const { id } = req.params;
|
|
256
|
+
if (executionResults.has(id)) {
|
|
257
|
+
const result = executionResults.get(id);
|
|
258
|
+
executionResults.delete(id); // Consume result
|
|
259
|
+
res.send({ status: 'completed', ...result });
|
|
260
|
+
} else {
|
|
261
|
+
res.send({ status: 'pending' });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
114
265
|
app.post('/command', (req, res) => {
|
|
115
266
|
const { type, path } = req.body;
|
|
116
267
|
if (!type) return res.status(400).send('Missing command type');
|
|
@@ -141,6 +292,11 @@ function startServer(port) {
|
|
|
141
292
|
continue;
|
|
142
293
|
}
|
|
143
294
|
|
|
295
|
+
if (isIgnored(filePath)) {
|
|
296
|
+
console.log(chalk.gray(`Skipping ignored file: ${filePath}`));
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
144
300
|
if (filePath.endsWith('.json')) {
|
|
145
301
|
try {
|
|
146
302
|
const json = JSON.parse(content);
|
|
@@ -181,6 +337,11 @@ function startServer(port) {
|
|
|
181
337
|
continue;
|
|
182
338
|
}
|
|
183
339
|
|
|
340
|
+
if (isIgnored(filePath)) {
|
|
341
|
+
console.log(chalk.gray(`Skipping ignored file deletion: ${filePath}`));
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
|
|
184
345
|
if (await fs.pathExists(safePath)) {
|
|
185
346
|
await fs.remove(safePath);
|
|
186
347
|
console.log(chalk.magenta(`Deleted: ${filePath}`));
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Roport Documentation
|
|
2
|
+
|
|
3
|
+
Roport is a two-way synchronization tool for Roblox development, designed to bridge the gap between Visual Studio Code and Roblox Studio. It allows developers to write code in external editors and have it instantly reflected in Roblox Studio, and vice versa.
|
|
4
|
+
|
|
5
|
+
This documentation provides a comprehensive reference for the CLI, Server API, Plugin features, and configuration formats.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Command Line Interface (CLI)
|
|
10
|
+
|
|
11
|
+
The `roport` CLI is the entry point for managing projects and running the sync server.
|
|
12
|
+
|
|
13
|
+
### `roport init`
|
|
14
|
+
Initializes a new Roport project in the current directory.
|
|
15
|
+
|
|
16
|
+
**Description:**
|
|
17
|
+
Scaffolds a standard project structure including:
|
|
18
|
+
- `default.project.json`: The project configuration file.
|
|
19
|
+
- `src/`: A standard directory structure for Roblox services (server, client, shared, etc.).
|
|
20
|
+
- `RoportSyncPlugin.rbxmx`: The Roblox Studio plugin file.
|
|
21
|
+
- `README.md`: Basic usage instructions.
|
|
22
|
+
|
|
23
|
+
**Usage:**
|
|
24
|
+
```bash
|
|
25
|
+
roport init
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### `roport serve`
|
|
29
|
+
Starts the synchronization server.
|
|
30
|
+
|
|
31
|
+
**Description:**
|
|
32
|
+
Launches an HTTP server that listens for connections from the Roblox Studio plugin. It also starts a file watcher to detect changes in the local file system.
|
|
33
|
+
|
|
34
|
+
**Options:**
|
|
35
|
+
- `-p, --port <number>`: Specifies the port to run the server on. Default is `3456`.
|
|
36
|
+
|
|
37
|
+
**Usage:**
|
|
38
|
+
```bash
|
|
39
|
+
roport serve
|
|
40
|
+
roport serve --port 8080
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 2. Server API Reference
|
|
46
|
+
|
|
47
|
+
The Roport server exposes a RESTful API used by the Roblox Studio plugin. All endpoints accept and return JSON.
|
|
48
|
+
|
|
49
|
+
### `GET /config`
|
|
50
|
+
Retrieves the project configuration.
|
|
51
|
+
|
|
52
|
+
**Description:**
|
|
53
|
+
Parses the `default.project.json` file and returns a flattened list of "mounts". A mount maps a Roblox service path (e.g., `ServerScriptService`) to a local file system path (e.g., `src/server`).
|
|
54
|
+
|
|
55
|
+
**Response:**
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"name": "ProjectName",
|
|
59
|
+
"mounts": [
|
|
60
|
+
{
|
|
61
|
+
"robloxPath": ["ServerScriptService"],
|
|
62
|
+
"filePath": "src/server",
|
|
63
|
+
"className": "Folder"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `GET /poll`
|
|
70
|
+
Checks for updates from the file system.
|
|
71
|
+
|
|
72
|
+
**Description:**
|
|
73
|
+
Called periodically by the plugin (Long Polling). Returns a list of files that have changed or been deleted locally since the last poll, and any queued commands.
|
|
74
|
+
|
|
75
|
+
**Response:**
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"changes": [
|
|
79
|
+
{ "filePath": "src/server/script.server.luau", "content": "print('Hello')" },
|
|
80
|
+
{ "filePath": "src/server/folder", "isDirectory": true }
|
|
81
|
+
],
|
|
82
|
+
"deletions": [
|
|
83
|
+
"src/server/old_script.server.luau"
|
|
84
|
+
],
|
|
85
|
+
"commands": []
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### `POST /batch`
|
|
90
|
+
Uploads a batch of files from Roblox Studio to the file system.
|
|
91
|
+
|
|
92
|
+
**Description:**
|
|
93
|
+
Used during "Sync All" or "Auto-Sync" from Studio. Writes multiple files to disk.
|
|
94
|
+
|
|
95
|
+
**Request Body:**
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"files": [
|
|
99
|
+
{ "filePath": "src/server/script.server.luau", "content": "..." }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `POST /delete`
|
|
105
|
+
Deletes files from the file system.
|
|
106
|
+
|
|
107
|
+
**Description:**
|
|
108
|
+
Triggered when an instance is deleted or renamed in Roblox Studio.
|
|
109
|
+
|
|
110
|
+
**Request Body:**
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"files": [
|
|
114
|
+
"src/server/deleted_script.server.luau"
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `POST /command`
|
|
120
|
+
Queues a command to be executed by the plugin.
|
|
121
|
+
|
|
122
|
+
**Description:**
|
|
123
|
+
Allows external tools to trigger actions in Roblox Studio (e.g., starting a playtest).
|
|
124
|
+
|
|
125
|
+
**Request Body:**
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"type": "PLAYTEST_START",
|
|
129
|
+
"path": null
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `POST /log`
|
|
134
|
+
Logs a message from Roblox Studio to the server console.
|
|
135
|
+
|
|
136
|
+
**Description:**
|
|
137
|
+
Useful for debugging plugin issues or forwarding Studio output to the terminal.
|
|
138
|
+
|
|
139
|
+
**Request Body:**
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"message": "Error in script...",
|
|
143
|
+
"stack": "Stack trace...",
|
|
144
|
+
"timestamp": 1678888888
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `GET /ping`
|
|
149
|
+
Health check endpoint.
|
|
150
|
+
|
|
151
|
+
**Description:**
|
|
152
|
+
Returns `pong`. Used by the plugin to detect if the server is online.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 3. AI Agent Integration
|
|
157
|
+
|
|
158
|
+
Roport includes features specifically designed to allow AI agents to control Roblox Studio autonomously.
|
|
159
|
+
|
|
160
|
+
### Log Streaming
|
|
161
|
+
The plugin automatically hooks into `LogService.MessageOut` and streams all Studio output to the Roport server console.
|
|
162
|
+
|
|
163
|
+
- **Format:** `[STUDIO HH:MM:SS] Message`
|
|
164
|
+
- **Colors:**
|
|
165
|
+
- White: Standard Output (`print`)
|
|
166
|
+
- Yellow: Warnings (`warn`)
|
|
167
|
+
- Red: Errors (`error`)
|
|
168
|
+
|
|
169
|
+
**Usage for AI:**
|
|
170
|
+
An AI agent running the server can monitor `stdout` to see runtime errors, debugging information, or game state printed by scripts.
|
|
171
|
+
|
|
172
|
+
### Remote Execution (REPL)
|
|
173
|
+
Allows an AI to execute arbitrary Luau code in the running Roblox Studio instance.
|
|
174
|
+
|
|
175
|
+
#### 1. Execute Code
|
|
176
|
+
**Endpoint:** `POST /execute`
|
|
177
|
+
|
|
178
|
+
**Request Body:**
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"code": "return workspace.Part.Position"
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Response:**
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"id": "1678889999000",
|
|
189
|
+
"status": "queued"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### 2. Get Result
|
|
194
|
+
**Endpoint:** `GET /execute/:id`
|
|
195
|
+
|
|
196
|
+
**Response (Pending):**
|
|
197
|
+
```json
|
|
198
|
+
{ "status": "pending" }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Response (Completed):**
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"status": "completed",
|
|
205
|
+
"success": true,
|
|
206
|
+
"result": "{\"X\":0,\"Y\":10,\"Z\":0}",
|
|
207
|
+
"error": null
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Usage for AI:**
|
|
212
|
+
1. **Inspect State:** Check properties of objects that aren't easily visible in files (e.g., physics state, player count).
|
|
213
|
+
2. **Unit Testing:** Run test functions and check the output.
|
|
214
|
+
3. **Manipulation:** Create temporary parts or modify the environment to test mechanics.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 4. Plugin Features
|
|
219
|
+
|
|
220
|
+
The `RoportSyncPlugin` is a Roblox Studio plugin that connects to the Roport server.
|
|
221
|
+
|
|
222
|
+
### Sync Panel
|
|
223
|
+
The main interface for the plugin.
|
|
224
|
+
- **Connect:** Establishes a connection to the server.
|
|
225
|
+
- **Sync All (Push):** Scans the entire Roblox workspace (based on configured mounts) and uploads all scripts and supported instances to the file system.
|
|
226
|
+
- **Pull Changes:** Manually fetches pending changes from the server.
|
|
227
|
+
- **Auto-Sync:** Toggles automatic uploading of changes made in Studio (scripts, properties, hierarchy) to the file system.
|
|
228
|
+
- **Auto-Pull:** Toggles automatic polling for changes made in the file system.
|
|
229
|
+
|
|
230
|
+
### Settings
|
|
231
|
+
Configurable via the "Settings" button in the panel.
|
|
232
|
+
- **Server Port:** Must match the port used in `roport serve`. Default: `3456`.
|
|
233
|
+
- **Sync Interval:** How often (in seconds) Auto-Sync checks for dirty scripts. Default: `2`.
|
|
234
|
+
|
|
235
|
+
### Synchronization Logic
|
|
236
|
+
- **Path Resolution:** The plugin uses the configuration from `/config` to determine where instances belong.
|
|
237
|
+
- **Two-Way Sync:**
|
|
238
|
+
- **Studio -> Disk:** Listens to `ScriptEditorService`, `AncestryChanged`, and `PropertyChangedSignal`.
|
|
239
|
+
- **Disk -> Studio:** Polls `/poll` for file changes and applies them using `Script.Source` or property updates.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 4. Configuration
|
|
244
|
+
|
|
245
|
+
### `default.project.json`
|
|
246
|
+
Defines the mapping between the file system and the Roblox DataModel.
|
|
247
|
+
|
|
248
|
+
**Structure:**
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"name": "MyProject",
|
|
252
|
+
"tree": {
|
|
253
|
+
"$className": "DataModel",
|
|
254
|
+
"ServerScriptService": {
|
|
255
|
+
"$className": "ServerScriptService",
|
|
256
|
+
"$path": "src/server"
|
|
257
|
+
},
|
|
258
|
+
"ReplicatedStorage": {
|
|
259
|
+
"$className": "ReplicatedStorage",
|
|
260
|
+
"$path": "src/shared"
|
|
261
|
+
}
|
|
262
|
+
// ... other services
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
- **$path:** The local directory path relative to the project root.
|
|
267
|
+
- **$className:** The Roblox ClassName for the container (usually a Service or Folder).
|
|
268
|
+
|
|
269
|
+
### `.rojoignore` / `.gitignore`
|
|
270
|
+
Specifies files and directories to exclude from synchronization.
|
|
271
|
+
|
|
272
|
+
**Supported Patterns:**
|
|
273
|
+
- `*.txt`: Ignore all text files.
|
|
274
|
+
- `dist/`: Ignore the `dist` directory.
|
|
275
|
+
- `src/private/**`: Recursive ignore.
|
|
276
|
+
- `node_modules`: Exact folder match.
|
|
277
|
+
|
|
278
|
+
**Default Ignores:**
|
|
279
|
+
- `.git`
|
|
280
|
+
- `node_modules`
|
|
281
|
+
- `cli`
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 5. File Type Support
|
|
286
|
+
|
|
287
|
+
Roport maps file extensions to Roblox Instance types.
|
|
288
|
+
|
|
289
|
+
### Scripts
|
|
290
|
+
- **`.server.luau` / `.server.lua`**: Creates a `Script` (RunContext=Legacy/Server).
|
|
291
|
+
- **`.client.luau` / `.client.lua`**: Creates a `LocalScript`.
|
|
292
|
+
- **`.luau` / `.lua`**: Creates a `ModuleScript`.
|
|
293
|
+
- **`init.server.luau`**: Creates a `Script` that is the parent of its directory's contents.
|
|
294
|
+
|
|
295
|
+
### Data
|
|
296
|
+
- **`.json`**: Creates a `ModuleScript` returning the JSON table (unless it is a meta file).
|
|
297
|
+
- **`.txt`**: Creates a `StringValue`.
|
|
298
|
+
|
|
299
|
+
### Models
|
|
300
|
+
- **`.rbxmx`**: Parses the Roblox XML Model format and reconstructs the Instance tree. Supports complex properties like `Vector3`, `Color3`, `UDim2`.
|
|
301
|
+
|
|
302
|
+
### Metadata
|
|
303
|
+
- **`.meta.json`**: Defines properties for a folder or specific instance.
|
|
304
|
+
- Example: `Folder/init.meta.json` sets properties for `Folder`.
|
|
305
|
+
- **`.model.json`**: Similar to meta, used for defining class types and properties for generic files.
|
|
306
|
+
|
|
307
|
+
**Supported Properties in JSON/XML:**
|
|
308
|
+
- Primitives: `string`, `boolean`, `number`
|
|
309
|
+
- Complex: `Vector3`, `Color3`, `UDim2`, `UDim`, `Rect`, `NumberRange`
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Roport Project
|
|
2
|
+
|
|
3
|
+
This project was initialized with [Roport](https://github.com/Rydaguy101/RojoExportPlugin).
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. **Install the Plugin:**
|
|
8
|
+
* Open Roblox Studio.
|
|
9
|
+
* Drag and drop `RoportSyncPlugin.rbxmx` into the 3D view.
|
|
10
|
+
* Right-click the created folder/script in the workspace and "Save as Local Plugin" (or just keep it in ServerStorage if you prefer).
|
|
11
|
+
|
|
12
|
+
2. **Start the Server:**
|
|
13
|
+
* Run `roport serve` in this directory.
|
|
14
|
+
* The server will start on port 3456.
|
|
15
|
+
|
|
16
|
+
3. **Connect:**
|
|
17
|
+
* In Roblox Studio, open the "Roport Sync" panel (Plugins tab).
|
|
18
|
+
* Click "Connect".
|
|
19
|
+
* Click "Sync All" to push the initial project structure to Roblox.
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
|
|
23
|
+
* `src/server`: Server-side scripts (`ServerScriptService`).
|
|
24
|
+
* `src/client`: Client-side scripts (`StarterPlayerScripts`).
|
|
25
|
+
* `src/shared`: Shared modules (`ReplicatedStorage`).
|
|
26
|
+
* `default.project.json`: Configuration for file paths.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
* **Two-Way Sync:** Edits in VS Code sync to Studio. Edits in Studio sync to VS Code (if Auto-Sync is on).
|
|
31
|
+
* **AI Integration:** Logs from Studio are streamed to the terminal. You can execute code remotely via the API.
|
|
32
|
+
|
|
33
|
+
## Documentation
|
|
34
|
+
|
|
35
|
+
For full details on the CLI, API, and Configuration, see [DOCUMENTATION.md](./DOCUMENTATION.md).
|