Skip to content

Commit e1c6088

Browse files
committed
core: Use same RNG algorithm as avmplus
1 parent 68d5a5f commit e1c6088

File tree

7 files changed

+74
-12
lines changed

7 files changed

+74
-12
lines changed

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: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::locale::get_current_date_time;
2+
3+
// https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/MathUtils.cpp#L1546
4+
const C1: i32 = 1376312589;
5+
const C2: i32 = 789221;
6+
const C3: i32 = 15731;
7+
const K_RANDOM_PURE_MAX: i32 = 0x7FFFFFFF;
8+
9+
#[derive(Debug, Clone, Copy, Default)]
10+
pub struct AvmRng {
11+
u_value: u32,
12+
u_xor_mask: u32,
13+
}
14+
15+
impl AvmRng {
16+
pub fn init_with_seed(&mut self, seed: u32) {
17+
self.u_value = seed;
18+
self.u_xor_mask = 0x48000000;
19+
}
20+
21+
fn random_fast_next(&mut self) -> i32 {
22+
if (self.u_value & 1) != 0 {
23+
self.u_value = (self.u_value >> 1) ^ self.u_xor_mask;
24+
} else {
25+
self.u_value >>= 1;
26+
}
27+
self.u_value as i32
28+
}
29+
30+
fn random_pure_hasher(self, mut i_seed: i32) -> i32 {
31+
i_seed = ((i_seed << 13) ^ i_seed).wrapping_sub(i_seed >> 21);
32+
33+
let mut i_result = i_seed.wrapping_mul(i_seed);
34+
i_result = i_result.wrapping_mul(C3);
35+
i_result = i_result.wrapping_add(C2);
36+
i_result = i_result.wrapping_mul(i_seed);
37+
i_result = i_result.wrapping_add(C1);
38+
i_result &= K_RANDOM_PURE_MAX;
39+
40+
i_result = i_result.wrapping_add(i_seed);
41+
42+
i_result = ((i_result << 13) ^ i_result).wrapping_sub(i_result >> 21);
43+
44+
i_result
45+
}
46+
47+
pub fn generate_random_number(&mut self) -> i32 {
48+
// In avmplus, RNG is initialized on first use.
49+
if self.u_value == 0 {
50+
let seed = get_seed();
51+
self.init_with_seed(seed);
52+
}
53+
54+
let mut a_num = self.random_fast_next();
55+
56+
a_num = self.random_pure_hasher(a_num.wrapping_mul(71));
57+
58+
a_num & K_RANDOM_PURE_MAX
59+
}
60+
}
61+
62+
// https://github.com/adobe-flash/avmplus/blob/65a05927767f3735db37823eebf7d743531f5d37/VMPI/PosixSpecificUtils.cpp#L18
63+
fn get_seed() -> u32 {
64+
get_current_date_time().timestamp_micros() as u32
65+
}

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
@@ -6,6 +6,7 @@ use crate::avm1::VariableDumper;
66
use crate::avm1::{Activation, ActivationIdentifier};
77
use crate::avm2::object::{EventObject as Avm2EventObject, Object as Avm2Object};
88
use crate::avm2::{Activation as Avm2Activation, Avm2, CallStack};
9+
use crate::avm_rng::AvmRng;
910
use crate::backend::ui::FontDefinition;
1011
use crate::backend::{
1112
audio::{AudioBackend, AudioManager},
@@ -38,7 +39,6 @@ use crate::library::Library;
3839
use crate::limits::ExecutionLimit;
3940
use crate::loader::{LoadBehavior, LoadManager};
4041
use crate::local_connection::LocalConnections;
41-
use crate::locale::get_current_date_time;
4242
use crate::net_connection::NetConnections;
4343
use crate::orphan_manager::OrphanManager;
4444
use crate::prelude::*;
@@ -54,7 +54,6 @@ use crate::DefaultFont;
5454
use async_channel::Sender;
5555
use gc_arena::lock::GcRefLock;
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)