Skip to content

Commit 56de10a

Browse files
committed
Add Processor::set_config for updating processor code after the VM has started
1 parent c2a192c commit 56de10a

File tree

1 file changed

+142
-81
lines changed

1 file changed

+142
-81
lines changed

src/vm/processor.rs

Lines changed: 142 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ pub type InstructionHook =
3939
#[derive(Derivative)]
4040
#[derivative(Debug)]
4141
pub struct Processor {
42-
instructions: Box<[Instruction]>,
42+
instructions: Vec<Instruction>,
4343
#[derivative(Debug = "ignore")]
4444
instruction_hook: Option<Box<InstructionHook>>,
4545
pub state: ProcessorState,
4646
}
4747

4848
impl Processor {
49-
pub fn late_init(
49+
pub(super) fn late_init(
5050
&mut self,
5151
vm: &LogicVM,
5252
building: &Building,
@@ -58,8 +58,7 @@ impl Processor {
5858
// ie. if a custom link name is specified for a building that would be built after this processor
5959
let mut taken_names = RapidHashMap::default();
6060

61-
let mut links = core::mem::take(&mut self.state.links).into_vec();
62-
links.retain_mut(|link| {
61+
self.state.links.retain_mut(|link| {
6362
// resolve the actual building at the link position
6463
// before this, link.building is just air
6564

@@ -126,7 +125,6 @@ impl Processor {
126125
}
127126
false // should never happen
128127
});
129-
self.state.links = links.into();
130128

131129
self.state
132130
.linked_positions
@@ -160,7 +158,112 @@ impl Processor {
160158
Ok(())
161159
}
162160

163-
pub fn do_tick(&mut self, vm: &LogicVM, time: f64, delta: f64) {
161+
/// Overwrites the code (and optionally the links) of this processor, resetting most internal state.
162+
///
163+
/// If an error occurs, all changes will be rolled back.
164+
pub fn set_config<T>(
165+
&mut self,
166+
code: T,
167+
links: Option<&[ProcessorLinkConfig]>,
168+
vm: &LogicVM,
169+
building: &Building,
170+
globals: &Constants,
171+
) -> VMLoadResult<()>
172+
where
173+
T: IntoIterator<Item = ast::Statement>,
174+
for<'a> &'a T: IntoIterator<Item = &'a ast::Statement>,
175+
{
176+
let prev_instructions = core::mem::take(&mut self.instructions);
177+
178+
// set_initial_config assumes the processor is disabled and increments running_processors if it becomes enabled
179+
// so decrement running_processors if the processor is currently enabled to avoid double-counting
180+
let prev_running_processors = Cell::get(&*vm.running_processors);
181+
if self.state.enabled {
182+
vm.running_processors.update(|n| n - 1);
183+
}
184+
185+
// this preserves any previous setrate calls, which matches Mindustry's behaviour
186+
let new_state = ProcessorState::new(self.state.privileged, self.state.ipt, vm);
187+
let prev_state = core::mem::replace(&mut self.state, new_state);
188+
189+
self.set_initial_config(code, links, vm, building.position);
190+
191+
// if the initialization fails, roll back the changes
192+
let result = self.late_init(vm, building, globals);
193+
if result.is_err() {
194+
let _ = core::mem::replace(&mut self.instructions, prev_instructions);
195+
vm.running_processors.set(prev_running_processors);
196+
let _ = core::mem::replace(&mut self.state, prev_state);
197+
}
198+
result
199+
}
200+
201+
/// Overwrites the code/links of this processor **without** fully initializing them. Assumes the processor is currently in its default state.
202+
fn set_initial_config<T>(
203+
&mut self,
204+
code: T,
205+
links: Option<&[ProcessorLinkConfig]>,
206+
vm: &LogicVM,
207+
position: PackedPoint2,
208+
) where
209+
T: IntoIterator<Item = ast::Statement>,
210+
for<'a> &'a T: IntoIterator<Item = &'a ast::Statement>,
211+
{
212+
let labels = {
213+
let mut labels = RapidHashMap::default();
214+
for statement in (&code).into_iter() {
215+
match statement {
216+
ast::Statement::Label(label) => {
217+
labels.insert(label.clone(), self.state.num_instructions);
218+
}
219+
ast::Statement::Instruction(_, _) => {
220+
self.state.num_instructions += 1;
221+
}
222+
}
223+
}
224+
Rc::new(labels)
225+
};
226+
227+
self.instructions.reserve_exact(self.state.num_instructions);
228+
for statement in code.into_iter() {
229+
if let ast::Statement::Instruction(instruction, _) = statement {
230+
self.instructions.push(
231+
InstructionBuilder {
232+
instruction,
233+
labels: labels.clone(),
234+
}
235+
.into(),
236+
);
237+
}
238+
}
239+
240+
self.state.enabled = !self.instructions.is_empty();
241+
if self.state.enabled {
242+
vm.running_processors.update(|n| n + 1);
243+
}
244+
245+
let fake_data = Rc::new(RefCell::new(BuildingData::Unknown {
246+
senseable_config: None,
247+
}));
248+
249+
if let Some(links) = links {
250+
self.state
251+
.links
252+
.extend(links.iter().map(|link| ProcessorLink {
253+
name: link.name.to_string(),
254+
building: Building {
255+
block: &content::blocks::AIR,
256+
position: PackedPoint2 {
257+
x: position.x + link.x,
258+
y: position.y + link.y,
259+
},
260+
data: fake_data.clone(),
261+
},
262+
}));
263+
}
264+
}
265+
266+
pub(super) fn do_tick(&mut self, vm: &LogicVM, time: f64, delta: f64) {
164267
if !self.state.enabled {
165268
return;
166269
}
@@ -220,7 +323,7 @@ pub struct ProcessorState {
220323

221324
privileged: bool,
222325
num_instructions: usize,
223-
links: Box<[ProcessorLink]>,
326+
links: Vec<ProcessorLink>,
224327
linked_positions: RapidHashSet<PackedPoint2>,
225328

226329
pub counter: usize,
@@ -241,6 +344,32 @@ pub struct ProcessorState {
241344
}
242345

243346
impl ProcessorState {
347+
fn new(privileged: bool, ipt: f64, vm: &LogicVM) -> Self {
348+
Self {
349+
enabled: false,
350+
stopped: false,
351+
wait_end_time: -1.,
352+
353+
privileged,
354+
num_instructions: 0,
355+
links: Vec::new(),
356+
linked_positions: RapidHashSet::default(),
357+
358+
counter: 0,
359+
accumulator: 0.,
360+
ipt,
361+
362+
running_processors: vm.running_processors.clone(),
363+
time: vm.time.clone(),
364+
printbuffer: U16String::new(),
365+
drawbuffer: Vec::new(),
366+
drawbuffer_len: 0,
367+
368+
locals: Constants::default(),
369+
variables: Variables::default(),
370+
}
371+
}
372+
244373
#[inline(always)]
245374
pub fn enabled(&self) -> bool {
246375
self.enabled
@@ -378,82 +507,14 @@ impl ProcessorBuilder<'_> {
378507
instruction_hook,
379508
} = self;
380509

381-
// TODO: this could be more efficient
382-
let mut num_instructions = 0;
383-
let labels = {
384-
let mut labels = RapidHashMap::default();
385-
for statement in &code {
386-
match statement {
387-
ast::Statement::Label(label) => {
388-
labels.insert(label.clone(), num_instructions);
389-
}
390-
ast::Statement::Instruction(_, _) => {
391-
num_instructions += 1;
392-
}
393-
}
394-
}
395-
Rc::new(labels)
510+
let mut processor = Processor {
511+
instructions: Vec::new(),
512+
instruction_hook,
513+
state: ProcessorState::new(privileged, ipt, &builder.vm),
396514
};
397515

398-
let mut instructions: Vec<Instruction> = Vec::with_capacity(num_instructions);
399-
for statement in code.into_iter() {
400-
if let ast::Statement::Instruction(instruction, _) = statement {
401-
instructions.push(
402-
InstructionBuilder {
403-
instruction,
404-
labels: labels.clone(),
405-
}
406-
.into(),
407-
);
408-
}
409-
}
516+
processor.set_initial_config(code, Some(links), &builder.vm, position);
410517

411-
let enabled = !instructions.is_empty();
412-
if enabled {
413-
builder.vm.running_processors.update(|n| n + 1);
414-
}
415-
416-
let fake_data = Rc::new(RefCell::new(BuildingData::Unknown {
417-
senseable_config: None,
418-
}));
419-
420-
let links = links
421-
.iter()
422-
.map(|link| ProcessorLink {
423-
name: link.name.to_string(),
424-
building: Building {
425-
block: &content::blocks::AIR,
426-
position: PackedPoint2 {
427-
x: position.x + link.x,
428-
y: position.y + link.y,
429-
},
430-
data: fake_data.clone(),
431-
},
432-
})
433-
.collect();
434-
435-
Box::new(Processor {
436-
instructions: instructions.into(),
437-
instruction_hook,
438-
state: ProcessorState {
439-
enabled,
440-
stopped: false,
441-
wait_end_time: -1.,
442-
privileged,
443-
num_instructions,
444-
links,
445-
linked_positions: RapidHashSet::default(),
446-
counter: 0,
447-
accumulator: 0.,
448-
ipt,
449-
running_processors: builder.vm.running_processors.clone(),
450-
time: builder.vm.time.clone(),
451-
printbuffer: U16String::new(),
452-
drawbuffer: Vec::new(),
453-
drawbuffer_len: 0,
454-
locals: Constants::default(),
455-
variables: Variables::default(),
456-
},
457-
})
518+
Box::new(processor)
458519
}
459520
}

0 commit comments

Comments
 (0)