Skip to content

Commit a97ff5e

Browse files
committed
Add CustomBuildingData trait to simplify implementing custom buildings
1 parent 81b9037 commit a97ff5e

File tree

4 files changed

+188
-59
lines changed

4 files changed

+188
-59
lines changed

src/logic/vm/buildings.rs

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use alloc::{boxed::Box, rc::Rc, string::ToString};
22
use core::cell::RefCell;
3+
use derivative::Derivative;
34

45
use strum_macros::IntoStaticStr;
56
use widestring::U16String;
67

78
use super::{
8-
LObject, LogicVMBuilder, Processor, ProcessorBuilder, VMLoadError, VMLoadResult,
9-
variables::LValue,
9+
LObject, LVar, LogicVM, LogicVMBuilder, Processor, ProcessorBuilder, ProcessorState,
10+
VMLoadError, VMLoadResult, instructions::InstructionResult, variables::LValue,
1011
};
1112
use crate::types::{
12-
Object, PackedPoint2,
13+
LAccess, Object, PackedPoint2,
1314
content::{self, Block},
1415
};
1516
#[cfg(feature = "std")]
@@ -207,14 +208,26 @@ impl PartialEq for Building {
207208
}
208209

209210
macro_rules! borrow_data {
211+
(
212+
mut $ref:expr,
213+
$state:ident : $bind:ident => $expr1:expr,
214+
$data:ident => $expr2:expr $(,)?
215+
) => {
216+
borrow_data!(
217+
@impl
218+
mut, (Rc::clone(&$ref).try_borrow_mut()),
219+
$bind, let $bind = &$state => $expr1,
220+
$data => $expr2
221+
)
222+
};
210223
(
211224
mut $ref:expr,
212225
$state:ident => $expr1:expr,
213226
$data:ident => $expr2:expr $(,)?
214227
) => {
215228
borrow_data!(
216229
@impl
217-
mut, $ref.try_borrow_mut(),
230+
mut, (Rc::clone(&$ref).try_borrow_mut()),
218231
$state => $expr1,
219232
$data => $expr2
220233
)
@@ -226,7 +239,7 @@ macro_rules! borrow_data {
226239
) => {
227240
borrow_data!(
228241
@impl
229-
$ref.try_borrow(),
242+
(Rc::clone(&$ref).try_borrow()),
230243
$bind, let $bind = &$state => $expr1,
231244
$data => $expr2
232245
)
@@ -238,7 +251,7 @@ macro_rules! borrow_data {
238251
) => {
239252
borrow_data!(
240253
@impl
241-
$ref.try_borrow(),
254+
(Rc::clone(&$ref).try_borrow()),
242255
$state => $expr1,
243256
$data => $expr2
244257
)
@@ -267,15 +280,16 @@ macro_rules! borrow_data {
267280

268281
pub(super) use borrow_data;
269282

270-
#[derive(Debug, IntoStaticStr)]
283+
#[derive(Derivative, IntoStaticStr)]
284+
#[derivative(Debug)]
271285
#[non_exhaustive]
272286
pub enum BuildingData {
273287
Processor(Box<Processor>),
274288
Memory(Box<[f64]>),
275289
Message(U16String),
276290
Switch(bool),
277291
Unknown { senseable_config: Option<LValue> },
278-
Custom(Object),
292+
Custom(#[derivative(Debug = "ignore")] Box<dyn CustomBuildingData>),
279293
}
280294

281295
impl BuildingData {
@@ -318,3 +332,64 @@ impl BuildingData {
318332
}
319333
}
320334
}
335+
336+
impl<T> From<T> for BuildingData
337+
where
338+
T: CustomBuildingData + 'static,
339+
{
340+
fn from(value: T) -> Self {
341+
Self::Custom(Box::new(value))
342+
}
343+
}
344+
345+
#[allow(unused_variables)]
346+
pub trait CustomBuildingData {
347+
#[must_use]
348+
fn read(
349+
&mut self,
350+
state: &mut ProcessorState,
351+
vm: &LogicVM,
352+
address: LValue,
353+
) -> Option<LValue> {
354+
None
355+
}
356+
357+
#[must_use]
358+
fn write(
359+
&mut self,
360+
state: &mut ProcessorState,
361+
vm: &LogicVM,
362+
address: LValue,
363+
value: LValue,
364+
) -> InstructionResult {
365+
InstructionResult::Ok
366+
}
367+
368+
#[must_use]
369+
fn printflush(&mut self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
370+
InstructionResult::Ok
371+
}
372+
373+
#[must_use]
374+
fn control(
375+
&mut self,
376+
state: &mut ProcessorState,
377+
vm: &LogicVM,
378+
control: LAccess,
379+
p1: &LVar,
380+
p2: &LVar,
381+
p3: &LVar,
382+
) -> InstructionResult {
383+
InstructionResult::Ok
384+
}
385+
386+
#[must_use]
387+
fn sensor(
388+
&mut self,
389+
state: &mut ProcessorState,
390+
vm: &LogicVM,
391+
sensor: LAccess,
392+
) -> Option<LValue> {
393+
None
394+
}
395+
}

src/logic/vm/instructions.rs

Lines changed: 95 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,14 @@ impl InstructionBuilder {
232232
p1,
233233
p2,
234234
p3,
235-
} => {
236-
lvar(p2);
237-
lvar(p3);
238-
Control {
239-
control,
240-
target: lvar(target),
241-
p1: lvar(p1),
242-
}
243-
.into()
235+
} => Control {
236+
control,
237+
target: lvar(target),
238+
p1: lvar(p1),
239+
p2: lvar(p2),
240+
p3: lvar(p3),
244241
}
242+
.into(),
245243
ast::Instruction::Sensor {
246244
result,
247245
target,
@@ -383,13 +381,13 @@ impl Read {
383381
}
384382

385383
impl SimpleInstructionTrait for Read {
386-
fn execute(&self, state: &mut ProcessorState, _: &LogicVM) {
384+
fn execute(&self, state: &mut ProcessorState, vm: &LogicVM) {
387385
let address = self.address.get(state);
388386
let target = self.target.get(state);
389387

390388
match target.obj() {
391389
Some(LObject::Building(building)) => borrow_data!(
392-
building.data.clone(),
390+
mut building.data,
393391
state: target_state => {
394392
// read variable with name, returning null for constants and undefined
395393
// or no-op if the address is not a string
@@ -427,8 +425,14 @@ impl SimpleInstructionTrait for Read {
427425
.setnum(state, Self::read_slice(message.as_slice(), &address));
428426
}
429427

428+
BuildingData::Custom(custom) => {
429+
if let Some(value) = custom.read(state, vm, address.into_owned()) {
430+
self.result.set(state, value);
431+
}
432+
}
433+
430434
// no-op if the target doesn't support reading
431-
_ => {},
435+
_ => {}
432436
},
433437
),
434438

@@ -451,14 +455,14 @@ pub struct Write {
451455
pub address: LVar,
452456
}
453457

454-
impl SimpleInstructionTrait for Write {
455-
fn execute(&self, state: &mut ProcessorState, _: &LogicVM) {
458+
impl InstructionTrait for Write {
459+
fn execute(&self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
456460
if let Some(LObject::Building(building)) = self.target.get(state).obj() {
457461
let address = self.address.get(state);
458462
let value = self.value.get_inner(state, &state.variables);
459463

460464
borrow_data!(
461-
mut Rc::clone(&building.data),
465+
mut building.data,
462466
state => {
463467
if let Some(LObject::String(name)) = address.into_owned().obj() {
464468
// @counter should never be in state.variables, since globals are checked first
@@ -473,16 +477,24 @@ impl SimpleInstructionTrait for Write {
473477
}
474478
}
475479
},
476-
data => {
477-
if let BuildingData::Memory(memory) = data
478-
&& let Ok(address) = address.num_usize()
479-
&& address < memory.len()
480-
{
481-
memory[address] = value.num();
480+
data => match data {
481+
BuildingData::Memory(memory) => {
482+
if let Ok(address) = address.num_usize()
483+
&& address < memory.len()
484+
{
485+
memory[address] = value.num();
486+
}
482487
}
483-
},
488+
489+
BuildingData::Custom(custom) => {
490+
return custom.write(state, vm, address.into_owned(), value.into_owned());
491+
}
492+
493+
_ => {}
494+
}
484495
);
485496
}
497+
InstructionResult::Ok
486498
}
487499
}
488500

@@ -595,19 +607,31 @@ pub struct PrintFlush {
595607
pub target: LVar,
596608
}
597609

598-
impl SimpleInstructionTrait for PrintFlush {
599-
fn execute(&self, state: &mut ProcessorState, _: &LogicVM) {
600-
if let Some(LObject::Building(target)) =
610+
impl InstructionTrait for PrintFlush {
611+
fn execute(&self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
612+
let result = if let Some(LObject::Building(target)) =
601613
self.target.get_inner(state, &state.variables).obj()
602-
&& let Ok(mut data) = target.data.try_borrow_mut()
603-
&& let BuildingData::Message(message_buffer) = &mut *data
614+
&& let Ok(mut data) = target.data.clone().try_borrow_mut()
604615
{
605616
if state.printbuffer.len() > MAX_TEXT_BUFFER {
606617
state.printbuffer.drain(MAX_TEXT_BUFFER..);
607618
}
608-
core::mem::swap(&mut state.printbuffer, message_buffer);
609-
}
619+
620+
match &mut *data {
621+
BuildingData::Message(message_buffer) => {
622+
core::mem::swap(&mut state.printbuffer, message_buffer);
623+
InstructionResult::Ok
624+
}
625+
626+
BuildingData::Custom(custom) => custom.printflush(state, vm),
627+
628+
_ => InstructionResult::Ok,
629+
}
630+
} else {
631+
InstructionResult::Ok
632+
};
610633
state.printbuffer.clear();
634+
result
611635
}
612636
}
613637

@@ -637,28 +661,40 @@ pub struct Control {
637661
pub control: LAccess,
638662
pub target: LVar,
639663
pub p1: LVar,
664+
pub p2: LVar,
665+
pub p3: LVar,
640666
}
641667

642-
impl SimpleInstructionTrait for Control {
643-
fn execute(&self, state: &mut ProcessorState, _: &LogicVM) {
644-
if self.control == LAccess::Enabled
645-
&& let Some(LObject::Building(building)) = self.target.get(state).obj()
668+
impl InstructionTrait for Control {
669+
fn execute(&self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
670+
if let Some(LObject::Building(building)) = self.target.get(state).obj()
646671
&& (state.privileged() || state.linked_positions().contains(&building.position))
647672
{
648-
let enabled = self.p1.get(state);
649-
if enabled.isnum() {
650-
let enabled = enabled.numf() != 0.;
651-
borrow_data!(
652-
mut Rc::clone(&building.data),
653-
state => state.set_enabled(enabled),
654-
data => {
655-
if let BuildingData::Switch(value) = data {
656-
*value = enabled;
673+
borrow_data!(
674+
mut building.data,
675+
state => if self.control == LAccess::Enabled {
676+
let enabled = self.p1.get(state);
677+
if enabled.isnum() {
678+
state.set_enabled(enabled.numf() != 0.);
679+
}
680+
},
681+
data => match data {
682+
BuildingData::Switch(value) if self.control == LAccess::Enabled => {
683+
let enabled = self.p1.get(state);
684+
if enabled.isnum() {
685+
*value = enabled.numf() != 0.;
657686
}
658-
},
659-
);
660-
}
687+
}
688+
689+
BuildingData::Custom(custom) => {
690+
return custom.control(state, vm, self.control, &self.p1, &self.p2, &self.p3);
691+
}
692+
693+
_ => {}
694+
},
695+
);
661696
}
697+
InstructionResult::Ok
662698
}
663699
}
664700

@@ -671,7 +707,7 @@ pub struct Sensor {
671707
}
672708

673709
impl SimpleInstructionTrait for Sensor {
674-
fn execute(&self, state: &mut ProcessorState, _: &LogicVM) {
710+
fn execute(&self, state: &mut ProcessorState, vm: &LogicVM) {
675711
use LAccess::*;
676712

677713
let target = self.target.get(state);
@@ -699,9 +735,9 @@ impl SimpleInstructionTrait for Sensor {
699735

700736
let result = match sensor.obj() {
701737
// normal sensors
702-
Some(LObject::Sensor(sensor)) => match target.obj() {
738+
&Some(LObject::Sensor(sensor)) => match target.obj() {
703739
// dead
704-
Some(LObject::Null) if *sensor == Dead => true.into(),
740+
Some(LObject::Null) if sensor == Dead => true.into(),
705741

706742
// senseable
707743
Some(LObject::Content(content)) => match content {
@@ -768,7 +804,7 @@ impl SimpleInstructionTrait for Sensor {
768804
PayloadType => setnull!(),
769805

770806
_ => borrow_data!(
771-
building.data.clone(),
807+
mut building.data,
772808
state => match sensor {
773809
LAccess::Enabled => state.enabled().into(),
774810
_ => setnull!(),
@@ -805,7 +841,16 @@ impl SimpleInstructionTrait for Sensor {
805841
_ => setnull!(),
806842
},
807843

808-
BuildingData::Custom(_) => setnull!(),
844+
BuildingData::Custom(custom) => {
845+
match custom.sensor(state, vm, sensor) {
846+
Some(value) => {
847+
self.result.set(state, value);
848+
return;
849+
}
850+
None if sensor == Enabled => true.into(),
851+
None => setnull!(),
852+
}
853+
},
809854

810855
BuildingData::Processor(_) => unreachable!(),
811856
},

0 commit comments

Comments
 (0)