mirror of
https://github.com/bdnugget/rs-sdk.git
synced 2026-11-07 09:07:30 +00:00
fix(client): refuse bare dialog continue while a choice is pending
The engine refuses RESUME_PAUSEBUTTON while if_addresumebutton options are registered (a choice must be answered with IF_BUTTON). Both clients still sent the bare RESUME and latched resumedPauseButton - and since the refused resume produces no new interface, nothing ever reset the latch: every later continue click returned client_rejected and the dialog was pinned until close. This is the 'toll dialogs only advance with sendClickDialog(0) once, then never again' failure fleets kept hitting (bug reports 2026-08-06 and 2026-08-10). Choice pages are exactly the ones publishing OK-typed (buttonType 1) options, so clickDialogOption now refuses a bare continue - and a CONTINUE-typed click - while any OK option is visible, without sending or latching. IF_BUTTON answers are unaffected. Verified live against the local engine (lite client): bare sendClickDialog(0) on the tutorial guide's choice page now returns client_rejected immediately and the choice remains answerable by text; plain continue pages still advance via their published index-1 option. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c4c22b395c
commit
db83d0425a
2 files changed
+22
-4
No files matched your search
@@ -2421,8 +2421,17 @@ export class Client extends GameShell {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A pending choice (OK-typed options, registered server-side via
|
||||
// if_addresumebutton) must be answered with IF_BUTTON on one of the
|
||||
// options - the engine refuses a bare RESUME while a choice is
|
||||
// pending. Sending one anyway latched resumedPauseButton with no new
|
||||
// interface arriving to reset it, pinning the dialog against every
|
||||
// later continue click. Refuse locally instead of desyncing.
|
||||
const choicePending = (): boolean =>
|
||||
this.getDialogOptions().some(o => o.buttonType === ButtonType.BUTTON_OK);
|
||||
|
||||
if (optionIndex === 0) {
|
||||
if (!this.resumedPauseButton && this.chatModalId !== -1) {
|
||||
if (!this.resumedPauseButton && this.chatModalId !== -1 && !choicePending()) {
|
||||
this.writePacketOpcode(ClientProt.RESUME_PAUSEBUTTON);
|
||||
this.out.p2(this.chatModalId);
|
||||
this.resumedPauseButton = true;
|
||||
@@ -2437,7 +2446,7 @@ export class Client extends GameShell {
|
||||
const option = dialogOptions[optionIndex - 1];
|
||||
|
||||
if (option.buttonType === ButtonType.BUTTON_CONTINUE) {
|
||||
if (!this.resumedPauseButton) {
|
||||
if (!this.resumedPauseButton && !choicePending()) {
|
||||
this.writePacketOpcode(ClientProt.RESUME_PAUSEBUTTON);
|
||||
this.out.p2(option.componentId);
|
||||
this.resumedPauseButton = true;
|
||||
|
||||
@@ -418,11 +418,20 @@ export function clickDialogOption(c: LiteClient, optionIndex = 0): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A pending choice (OK-typed options, registered server-side via
|
||||
// if_addresumebutton) must be answered with IF_BUTTON on one of the
|
||||
// options - the engine refuses a bare RESUME while a choice is pending.
|
||||
// Sending one anyway latched resumedPauseButton with no new interface
|
||||
// arriving to reset it, pinning the dialog against every later continue
|
||||
// click. Refuse locally instead of desyncing.
|
||||
const choicePending = (): boolean =>
|
||||
getDialogOptions(c).some(o => o.buttonType === ButtonType.BUTTON_OK);
|
||||
|
||||
if (optionIndex === 0) {
|
||||
// Bare "continue". The resumedPauseButton guard mirrors the engine's
|
||||
// ResumePauseButtonHandler fix: sending a second RESUME while one is
|
||||
// pending re-picks the previous choice.
|
||||
if (!c.resumedPauseButton && c.chatModalId !== -1) {
|
||||
if (!c.resumedPauseButton && c.chatModalId !== -1 && !choicePending()) {
|
||||
c.writeOpcode(ClientProt.RESUME_PAUSEBUTTON);
|
||||
c.out.p2(c.chatModalId);
|
||||
c.resumedPauseButton = true;
|
||||
@@ -442,7 +451,7 @@ export function clickDialogOption(c: LiteClient, optionIndex = 0): boolean {
|
||||
|
||||
const option = options[optionIndex - 1];
|
||||
if (option.buttonType === ButtonType.BUTTON_CONTINUE) {
|
||||
if (c.resumedPauseButton) {
|
||||
if (c.resumedPauseButton || choicePending()) {
|
||||
return false;
|
||||
}
|
||||
c.writeOpcode(ClientProt.RESUME_PAUSEBUTTON);
|
||||
|
||||
Reference in new issue
Block a user