Skip to content

Commit ec72197

Browse files
committed
core: Use same RNG algorithm as avmplus
1 parent fc95636 commit ec72197

File tree

9 files changed

+126
-14
lines changed

9 files changed

+126
-14
lines changed

Cargo.lock

Lines changed: 13 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ either = "1.15.0"
6969
chardetng = "0.1.17"
7070
tracy-client = { version = "0.18.2", optional = true, default-features = false }
7171

72+
[target.'cfg(target_os = "windows")'.dependencies]
73+
windows-sys = { version = "0.61.2", features = ["Win32_System_Performance"] }
74+
75+
[target.'cfg(target_os = "macos")'.dependencies]
76+
mach2 = "0.5.0"
77+
7278
[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
7379
workspace = true
7480

core/src/avm1/activation.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use crate::vminterface::Instantiator;
1818
use crate::{avm_error, avm_warn};
1919
use gc_arena::{Gc, Mutation};
2020
use indexmap::IndexMap;
21-
use rand::Rng;
2221
use ruffle_macros::istr;
2322
use std::borrow::Cow;
2423
use std::cell::Cell;
@@ -1831,7 +1830,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
18311830
// The max value is clamped to the range [0, 2^31 - 1).
18321831
let max = self.context.avm1.pop().coerce_to_f64(self)? as i32;
18331832
let result = if max > 0 {
1834-
self.context.rng.random_range(0..max)
1833+
self.context.rng.generate_random_number() % max
18351834
} else {
18361835
0
18371836
};

core/src/avm1/globals/math.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::avm1::error::Error;
33
use crate::avm1::property_decl::{DeclContext, Declaration};
44
use crate::avm1::{Object, Value};
55

6-
use rand::Rng;
76
use std::f64::consts;
87

98
macro_rules! wrap_std {
@@ -161,7 +160,7 @@ pub fn random<'gc>(
161160
// See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/MathUtils.cpp#L1731C24-L1731C44
162161
// This generated a restricted set of 'f64' values, which some SWFs implicitly rely on.
163162
const MAX_VAL: u32 = 0x7FFFFFFF;
164-
let rand = activation.context.rng.random_range(0..MAX_VAL);
163+
let rand = activation.context.rng.generate_random_number();
165164
Ok(((rand as f64) / (MAX_VAL as f64 + 1f64)).into())
166165
}
167166

core/src/avm2/globals/math.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::avm2::parameters::ParametersExt;
77
use crate::avm2::value::Value;
88
use crate::avm2::{ClassObject, Error};
99
use num_traits::ToPrimitive;
10-
use rand::Rng;
1110

1211
macro_rules! wrap_std {
1312
($name:ident, $std:expr) => {
@@ -159,6 +158,6 @@ pub fn random<'gc>(
159158
// See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/MathUtils.cpp#L1731C24-L1731C44
160159
// This generated a restricted set of 'f64' values, which some SWFs implicitly rely on.
161160
const MAX_VAL: u32 = 0x7FFFFFFF;
162-
let rand = activation.context.rng.random_range(0..MAX_VAL);
161+
let rand = activation.context.rng.generate_random_number();
163162
Ok(((rand as f64) / (MAX_VAL as f64 + 1f64)).into())
164163
}

core/src/avm_rng.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#[cfg(windows)]
2+
use windows_sys::Win32::System::Performance::QueryPerformanceCounter;
3+
4+
#[cfg(target_os = "macos")]
5+
use mach2::mach_time::mach_absolute_time;
6+
7+
#[cfg(not(any(windows, target_os = "macos")))]
8+
use crate::locale::get_current_date_time;
9+
10+
// https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/MathUtils.cpp#L1546
11+
const C1: i32 = 1376312589;
12+
const C2: i32 = 789221;
13+
const C3: i32 = 15731;
14+
const K_RANDOM_PURE_MAX: i32 = 0x7FFFFFFF;
15+
16+
#[derive(Debug, Clone, Copy)]
17+
pub struct AvmRng {
18+
u_value: u32,
19+
u_xor_mask: u32,
20+
}
21+
22+
impl Default for AvmRng {
23+
fn default() -> Self {
24+
Self {
25+
u_value: 0, // 0 means uninitialized
26+
u_xor_mask: 0,
27+
}
28+
}
29+
}
30+
31+
impl AvmRng {
32+
pub fn init_with_seed(&mut self, seed: u32) {
33+
self.u_value = seed;
34+
self.u_xor_mask = 0x48000000;
35+
}
36+
37+
fn random_fast_next(&mut self) -> i32 {
38+
if (self.u_value & 1) != 0 {
39+
self.u_value = (self.u_value >> 1) ^ self.u_xor_mask;
40+
} else {
41+
self.u_value >>= 1;
42+
}
43+
self.u_value as i32
44+
}
45+
46+
fn random_pure_hasher(&self, mut i_seed: i32) -> i32 {
47+
i_seed = ((i_seed << 13) ^ i_seed).wrapping_sub(i_seed >> 21);
48+
49+
let mut i_result = i_seed.wrapping_mul(i_seed);
50+
i_result = i_result.wrapping_mul(C3);
51+
i_result = i_result.wrapping_add(C2);
52+
i_result = i_result.wrapping_mul(i_seed);
53+
i_result = i_result.wrapping_add(C1);
54+
i_result = i_result & K_RANDOM_PURE_MAX;
55+
56+
i_result = i_result.wrapping_add(i_seed);
57+
58+
i_result = ((i_result << 13) ^ i_result).wrapping_sub(i_result >> 21);
59+
60+
i_result
61+
}
62+
63+
pub fn generate_random_number(&mut self) -> i32 {
64+
// In avmplus, RNG is initialized on first use.
65+
if self.u_value == 0 {
66+
let seed = get_seed();
67+
self.init_with_seed(seed);
68+
}
69+
70+
let mut a_num = self.random_fast_next();
71+
72+
a_num = self.random_pure_hasher(a_num.wrapping_mul(71));
73+
74+
a_num & K_RANDOM_PURE_MAX
75+
}
76+
}
77+
78+
// https://github.com/adobe-flash/avmplus/blob/65a05927767f3735db37823eebf7d743531f5d37/VMPI/WinPortUtils.cpp#L305
79+
#[cfg(windows)]
80+
fn get_seed() -> u32 {
81+
let mut value: i64 = 0;
82+
unsafe {
83+
QueryPerformanceCounter(&mut value);
84+
}
85+
value as u32
86+
}
87+
88+
// https://github.com/adobe-flash/avmplus/blob/65a05927767f3735db37823eebf7d743531f5d37/VMPI/MacPortUtils.cpp#L31
89+
#[cfg(target_os = "macos")]
90+
fn get_seed() -> u32 {
91+
unsafe { mach_absolute_time() as u32 }
92+
}
93+
94+
// https://github.com/adobe-flash/avmplus/blob/65a05927767f3735db37823eebf7d743531f5d37/VMPI/PosixSpecificUtils.cpp#L18
95+
#[cfg(not(any(windows, target_os = "macos")))]
96+
fn get_seed() -> u32 {
97+
get_current_date_time().timestamp_micros() as u32
98+
}

core/src/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::avm1::{Object as Avm1Object, Value as Avm1Value};
88
use crate::avm2::api_version::ApiVersion;
99
use crate::avm2::Activation as Avm2Activation;
1010
use crate::avm2::{Avm2, LoaderInfoObject, Object as Avm2Object, SoundChannelObject};
11+
use crate::avm_rng::AvmRng;
1112
use crate::backend::{
1213
audio::{AudioBackend, AudioManager, SoundHandle, SoundInstanceHandle},
1314
log::LogBackend,
@@ -43,7 +44,6 @@ use crate::PlayerMode;
4344
use async_channel::Sender;
4445
use core::fmt;
4546
use gc_arena::{Collect, Mutation};
46-
use rand::rngs::SmallRng;
4747
use ruffle_render::backend::{BitmapCacheEntry, RenderBackend};
4848
use ruffle_render::commands::{CommandHandler, CommandList};
4949
use ruffle_render::transform::TransformStack;
@@ -119,7 +119,7 @@ pub struct UpdateContext<'gc> {
119119
pub video: &'gc mut dyn VideoBackend,
120120

121121
/// The RNG, used by the AVM `RandomNumber` opcode, `Math.random(),` and `random()`.
122-
pub rng: &'gc mut SmallRng,
122+
pub rng: &'gc mut AvmRng,
123123

124124
/// The current player's stage (including all loaded levels)
125125
pub stage: Stage<'gc>,

core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern crate num_derive;
1515
#[macro_use]
1616
mod avm1;
1717
mod avm2;
18+
mod avm_rng;
1819
mod binary_data;
1920
pub mod bitmap;
2021
pub mod buffer;

core/src/player.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ use crate::library::Library;
3838
use crate::limits::ExecutionLimit;
3939
use crate::loader::{LoadBehavior, LoadManager};
4040
use crate::local_connection::LocalConnections;
41-
use crate::locale::get_current_date_time;
4241
use crate::net_connection::NetConnections;
4342
use crate::orphan_manager::OrphanManager;
4443
use crate::prelude::*;
@@ -53,8 +52,8 @@ use crate::vminterface::Instantiator;
5352
use crate::DefaultFont;
5453
use async_channel::Sender;
5554
use gc_arena::lock::GcRefLock;
55+
use crate::avm_rng::AvmRng;
5656
use gc_arena::{Collect, DynamicRootSet, Mutation, Rootable};
57-
use rand::{rngs::SmallRng, SeedableRng};
5857
use ruffle_macros::istr;
5958
use ruffle_render::backend::{null::NullRenderer, RenderBackend, ViewportDimensions};
6059
use ruffle_render::commands::CommandList;
@@ -315,7 +314,7 @@ pub struct Player {
315314

316315
transform_stack: TransformStack,
317316

318-
rng: SmallRng,
317+
rng: AvmRng,
319318

320319
gc_arena: Rc<RefCell<GcArena>>,
321320

@@ -2949,7 +2948,7 @@ impl PlayerBuilder {
29492948
mouse_cursor_needs_check: false,
29502949

29512950
// Misc. state
2952-
rng: SmallRng::seed_from_u64(get_current_date_time().timestamp_millis() as u64),
2951+
rng: AvmRng::default(),
29532952
system: SystemProperties::new(language),
29542953
page_url: self.page_url.clone(),
29552954
transform_stack: TransformStack::new(),

0 commit comments

Comments
 (0)