Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try several sockets #14

Merged
merged 7 commits into from
Jul 9, 2024
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ All notable changes to this project will be documented in this file.
- Warp to file is now the title action, as this seems to be what people expect
- Many bugs were found and fixed along the way, but I'm not sure if they're
worth mentioning.
- Overhaul socket handling. Now supports many Visual Studio Code workspaces!
As a result of this, the setting `renpy.webSocketsPort` was replaced with
`renpy.socketPorts`, which is an array of ports to try to connect to. The
first one that works will be used.

## 1.2.0 - 2024-07-06

Expand Down
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 17 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,22 @@
"markdownDescription": "The interval in milliseconds to execute `renpy.warp_to_line()` when in _Follow Cursor_ mode.\n\nThe lower the value, the more frequently the command is issued, which can feel more responsive at the cost of increased processing.\n\nNeeds a restart to take effect.",
"scope": "machine-overridable"
},
"renpyWarp.webSocketsPort": {
"type": "integer",
"default": 40124,
"minimum": 1,
"maximum": 65535,
"markdownDescription": "The port to use for the WebSocket server.\n\nNeeds a restart to take effect.",
"renpyWarp.socketPorts": {
"type": "array",
"default": [
40111,
40112,
40113,
40114,
40115,
40116,
40117,
40118,
40119,
40120
],
"minItems": 1,
"markdownDescription": "Ports to attempt to use to for the WebSocket server.",
"scope": "machine-overridable"
},
"renpyWarp.renpyExtensionsEnabled": {
Expand Down Expand Up @@ -257,6 +267,7 @@
},
"dependencies": {
"adm-zip": "^0.5.14",
"get-port": "^5.1.1",
"node-window-manager": "^2.2.4",
"p-throttle": "^4.1.1",
"pidtree": "^0.6.0",
Expand Down
5 changes: 0 additions & 5 deletions src/lib/follow_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { get_logger } from './logger'
import path from 'upath'
import p_throttle from 'p-throttle'
import { find_game_root } from './sh'
import { ensure_websocket_server } from './socket'

const logger = get_logger()
const last_warps = new Map<number, string>()
Expand Down Expand Up @@ -158,10 +157,6 @@ export class FollowCursor {
'statusBarItem.warningBackground'
)

// TODO: handle errors
await ensure_websocket_server({ pm: this.pm })
await process.wait_for_socket()

this.text_editor_handle?.dispose()
this.text_editor_handle = vscode.window.onDidChangeTextEditorSelection(
async (event) => {
Expand Down
265 changes: 153 additions & 112 deletions src/lib/launch.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as vscode from 'vscode'
import path from 'upath'
import { promisify } from 'node:util'

import { focus_window, ProcessManager, RenpyProcess } from './process'
import { FollowCursor, sync_editor_with_renpy } from './follow_cursor'
import { get_config } from './util'
import { get_logger } from './logger'
import { find_game_root, get_renpy_sh, make_cmd } from './sh'
import { has_any_rpe, has_current_rpe, install_rpe } from './rpe'
import { start_websocket_server, get_open_port } from './socket'

const logger = get_logger()

Expand Down Expand Up @@ -113,135 +113,176 @@ export async function launch_renpy({
} else {
logger.info("opening new ren'py window")

await vscode.window.withProgress(
{
title: "Starting Ren'Py" + (intent ? ' ' + intent : ''),
location: vscode.ProgressLocation.Notification,
cancellable: true,
},
async (progress, cancel) => {
const renpy_sh = await get_renpy_sh({
WARP_ENABLED: extensions_enabled ? '1' : undefined,
WARP_WS_PORT: get_config('webSocketsPort'),
})
if (!renpy_sh) return {}

let cmd: string
try {
pm.show_loading = true
pm.update_status_bar()

const socket_port = await get_open_port()

const renpy_sh = await get_renpy_sh({
WARP_ENABLED: extensions_enabled ? '1' : undefined,
WARP_WS_PORT: socket_port.toString(),
})
if (!renpy_sh) throw new Error('no renpy.sh found')

let cmd: string

if (line === undefined) {
cmd = renpy_sh + ' ' + make_cmd([game_root])
} else {
cmd =
renpy_sh +
' ' +
make_cmd([
game_root,
'--warp',
`${filename_relative}:${line + 1}`,
])
}

if (line === undefined) {
cmd = renpy_sh + ' ' + make_cmd([game_root])
} else {
cmd =
renpy_sh +
' ' +
make_cmd([
if (extensions_enabled) {
if (!(await has_any_rpe())) {
const selection =
await vscode.window.showInformationMessage(
`Ren'Py Launch and Sync can install a script in your Ren'Py project to synchronize the game and editor. Would you like to install it?`,
'Yes, install',
'No, do not install',
'Cancel'
)
if (selection === 'Yes, install') {
const installed_path = await install_rpe({
game_root,
'--warp',
`${filename_relative}:${line + 1}`,
])
}
progress.report({ increment: 33 })

if (extensions_enabled) {
if (!(await has_any_rpe())) {
const selection =
await vscode.window.showInformationMessage(
`Ren'Py Launch and Sync can install a script in your Ren'Py project to synchronize the game and editor. Would you like to install it?`,
'Yes, install',
'No, do not install'
)
if (selection === 'Yes, install') {
await install_rpe({ game_root, context })
} else {
extensions_enabled = false
await vscode.workspace
.getConfiguration('renpyWarp')
.update('renpyExtensionsEnabled', false, true)

vscode.window.showInformationMessage(
'No RPE script will be installed. Keep in mind that some features may not work as expected.',
'OK'
)
}
} else if (!(await has_current_rpe(renpy_sh))) {
await install_rpe({ game_root, context })
context,
})
const relative_path = path.relative(
vscode.workspace.workspaceFolders?.[0].uri.fsPath ??
game_root,
installed_path
)
vscode.window.showInformationMessage(
`Ren'Py Extensions were installed at ${relative_path}`,
'OK'
)
} else if (selection === 'No, do not install') {
extensions_enabled = false
await vscode.workspace
.getConfiguration('renpyWarp')
.update('renpyExtensionsEnabled', false, true)

vscode.window.showInformationMessage(
"Ren'Py extensions in this project have been updated.",
'No RPE script will be installed. Keep in mind that some features may not work as expected.',
'OK'
)
} else if (is_development_mode) {
await install_rpe({ game_root, context })
} else {
throw new Error('user cancelled')
}
} else if (!(await has_current_rpe(renpy_sh))) {
await install_rpe({ game_root, context })
vscode.window.showInformationMessage(
"Ren'Py extensions in this project have been updated.",
'OK'
)
} else if (is_development_mode) {
await install_rpe({ game_root, context })
}

progress.report({ increment: 33 })

if (strategy === 'Replace Window') pm.kill_all()

const rpp = new RenpyProcess({
cmd,
game_root,
async message_handler(message) {
if (message.type === 'current_line') {
logger.debug(
`current line reported as ${message.line} in ${message.relative_path}`
)
if (!follow_cursor.active) return

await sync_editor_with_renpy({
path: message.path,
relative_path: message.relative_path,
line: message.line - 1,
})
} else {
logger.warn('unhandled message:', message)
}
},
context,
await start_websocket_server({
pm,
port: socket_port,
})
pm.add(rpp)
}

cancel.onCancellationRequested(() => {
rpp?.kill()
})
if (strategy === 'Replace Window') pm.kill_all()

return await vscode.window.withProgress(
{
title: "Starting Ren'Py" + (intent ? ' ' + intent : ''),
location: vscode.ProgressLocation.Notification,
cancellable: true,
},
async (progress, cancel) => {
const rpp = new RenpyProcess({
cmd,
game_root,
socket_port,
async message_handler(message) {
if (message.type === 'current_line') {
logger.debug(
`current line reported as ${message.relative_path}:${message.line}`
)
if (!follow_cursor.active) return

await sync_editor_with_renpy({
path: message.path,
relative_path: message.relative_path,
line: message.line - 1,
})
} else {
logger.warn('unhandled message:', message)
}
},
context,
pm,
})
pm.add(rpp)

if (
!follow_cursor.active &&
extensions_enabled &&
get_config('followCursorOnLaunch') &&
pm.length === 1
) {
logger.info('enabling follow cursor on launch')
await follow_cursor.enable()
}

if (
!follow_cursor.active &&
extensions_enabled &&
get_config('followCursorOnLaunch') &&
pm.length === 1
) {
logger.info('enabling follow cursor on launch')
await follow_cursor.enable()
}
if (
pm.length > 1 &&
follow_cursor.active &&
strategy !== 'Replace Window'
) {
follow_cursor.disable()
vscode.window.showInformationMessage(
"Follow cursor was disabled because multiple Ren'Py instances are running",
'OK'
)
}

if (
pm.length > 1 &&
follow_cursor.active &&
strategy !== 'Replace Window'
) {
follow_cursor.disable()
vscode.window.showInformationMessage(
"Follow cursor was disabled because multiple Ren'Py instances are running",
'OK'
)
}
cancel.onCancellationRequested(() => {
pm.show_loading = false
rpp?.kill()
})

if (extensions_enabled) {
logger.info('waiting for process to connect to socket')

if (extensions_enabled) {
logger.info('waiting for process to connect to socket')
progress.report({
message: 'Waiting for socket connection',
})

progress.report({ increment: 33 })
while (!rpp.socket && !rpp.dead) {
await new Promise((resolve) =>
setTimeout(resolve, 100)
)
}
if (rpp.dead) throw new Error('panic')

while (!rpp.socket) {
await new Promise((resolve) => setTimeout(resolve, 100))
logger.debug('process connected to socket first time')
}

logger.info('process connected')
}
progress.report({ message: '' })

return rpp
}
)
pm.show_loading = false
pm.update_status_bar()

return rpp
}
)
} catch (e) {
throw e
} finally {
pm.show_loading = false
pm.update_status_bar()
}
}
}
Loading
Loading