@@ -39,14 +39,14 @@ pub type InstructionHook =
3939#[ derive( Derivative ) ]
4040#[ derivative( Debug ) ]
4141pub 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
4848impl 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
243346impl 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