Skip to content
Open
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 core/src/bundle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// See core/src/definition.rs for why this is here: the #[bitfield] macro (modular_bitfield)
// triggers a false-positive "unnecessary parentheses" lint on this rustc version.
#![allow(unused_parens)]

use std::hash::Hash;
use std::io::Seek;
use std::marker::PhantomData;
Expand Down
5 changes: 5 additions & 0 deletions core/src/definition.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// The #[bitfield] macro (modular_bitfield) below generates code that this rustc version
// misreports as "unnecessary parentheses" on the annotated fields themselves; none of the
// fields actually contain parens, so this is a lint false positive tied to the macro expansion.
#![allow(unused_parens)]

use std::path::PathBuf;
use std::{fmt, io};

Expand Down
19 changes: 18 additions & 1 deletion scc/lib/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,26 @@ pub extern "C" fn scc_settings_disable_error_popup(settings: &mut SccSettings) {
settings.show_error_popup = false;
}

// A panic deep in the compiler (e.g. triggered by a script that doesn't match the current
// game version) used to unwind straight across this extern "C" boundary into the native host
// (RED4ext) - that's undefined behavior and crashed the whole game for us, instead of just
// failing the compilation with an error message. catch_unwind stops it here and turns it into
// a normal SccResult::Error.
#[unsafe(no_mangle)]
pub extern "C" fn scc_compile(settings: Box<SccSettings>) -> Box<SccResult> {
compile(&settings)
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| compile(&settings))) {
Ok(result) => result,
Err(panic) => {
let message = panic
.downcast_ref::<&str>()
.map(|s| (*s).to_string())
.or_else(|| panic.downcast_ref::<String>().cloned())
.unwrap_or_else(|| "unknown internal error".to_string());
Box::new(SccResult::Error(anyhow::anyhow!(
"internal compiler error (this is a bug, please report it): {message}"
)))
}
}
}

#[unsafe(no_mangle)]
Expand Down
8 changes: 6 additions & 2 deletions scc/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{fmt, io, iter, vec};
use anyhow::Context;
use api::{SccOutput, SccResult, SccSettings};
use fd_lock::RwLock;
use flexi_logger::{Age, Cleanup, Criterion, Duplicate, FileSpec, LogSpecBuilder, Logger, Naming};
use flexi_logger::{Age, Cleanup, Criterion, FileSpec, LogSpecBuilder, Logger, Naming};
use hashbrown::{HashMap, HashSet};
use hints::UserHints;
use log::LevelFilter;
Expand Down Expand Up @@ -239,11 +239,15 @@ fn try_compile_files(
}
}

// FIX (2026-08-03): scc_compile runs in-process inside the game's GUI executable, which has no
// console and thus no valid stdout handle. duplicate_to_stdout tried to write there anyway, and
// when that write failed flexi_logger's own error-reporting path (which also goes through
// stdout) failed too, causing flexi_logger to panic ("error output channel itself is broken").
// File logging alone is all that's meaningful here anyway - nothing reads scc's stdout in-game.
fn setup_logger(r6_dir: &Path) {
let file = FileSpec::default().directory(r6_dir.join("logs")).basename("redscript");
Logger::with(LogSpecBuilder::new().default(LevelFilter::Info).build())
.log_to_file(file)
.duplicate_to_stdout(Duplicate::All)
.rotate(Criterion::Age(Age::Day), Naming::Timestamps, Cleanup::KeepLogFiles(4))
.format(|out, time, msg| write!(out, "[{} - {}] {}", msg.level(), time.now().to_rfc2822(), msg.args()))
.start()
Expand Down
Loading