Merge pull request #34 from Ryex/vm-rebuild

Vm rebuild
This commit is contained in:
Rachel Powers
2024-09-17 15:17:17 -07:00
committed by GitHub
198 changed files with 173860 additions and 132425 deletions

View File

@@ -1,4 +1,7 @@
{
"rust-analyzer.check.allTargets": false,
"rust-analyzer.cargo.target": "wasm32-unknown-unknown"
"rust-analyzer.cargo.target": "wasm32-unknown-unknown",
"rust-analyzer.cargo.features": [
"tsify",
]
}

552
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,11 @@
[workspace]
members = ["ic10lsp_wasm", "ic10emu_wasm", "ic10emu", "xtask"]
members = [
"ic10lsp_wasm",
"stationeers_data",
"ic10emu",
"ic10emu_wasm",
"xtask",
]
resolver = "2"
[workspace.package]

View File

@@ -1 +1,222 @@
{"language":"en","flagWords":[],"version":"0.2","words":["Astroloy","Autolathe","bapal","bapz","bapzal","batchmode","batchmodes","bdns","bdnsal","bdse","bdseal","beqal","beqz","beqzal","bgeal","bgez","bgezal","bgtal","bgtz","bgtzal","bindgen","bleal","blez","blezal","bltal","bltz","bltzal","bnaal","bnan","bnaz","bnazal","bneal","bnez","bnezal","brap","brapz","brdns","brdse","breq","breqz","brge","brgez","brgt","brgtz","brle","brlez","brlt","brltz","brna","brnan","brnaz","brne","brnez","Circuitboard","codegen","conv","cstyle","endpos","getd","Hardsuit","hashables","inext","inextp","infile","itertools","jetpack","kbshortcutmenu","Keybind","lbns","logicable","logictype","logictypes","lzma","Mineables","mscorlib","MSEED","ninf","nomatch","oprs","overcolumn","Overlength","pedia","peekable","prec","preproc","putd","QUICKFIX","reagentmode","reagentmodes","repr","retval","rocketstation","sapz","sattellite","sdns","sdse","searchbox","searchbtn","seqz","serde","settingsmenu","sgez","sgtz","slez","slotlogic","slotlogicable","slotlogictype","slotlogictypes","slottype","sltz","snan","snanz","snaz","snez","splitn","Stationeers","stationpedia","stdweb","thiserror","tokentype","trunc","Tsify","whos","Depressurising","Pressurising","logicslottypes","lparen","rparen","hstack","dylib"]}
{
"language": "en",
"flagWords": [],
"version": "0.2",
"words": [
"Agrizero",
"aliasable",
"Analyizer",
"Analyser",
"arn't",
"Astroloy",
"Asura",
"Atmo",
"autoignition",
"Autolathe",
"Autominer",
"Autotagged",
"bapal",
"bapz",
"bapzal",
"batchmode",
"batchmodes",
"bdns",
"bdnsal",
"bdse",
"bdseal",
"beqal",
"beqz",
"beqzal",
"bgeal",
"bgez",
"bgezal",
"bgtal",
"bgtz",
"bgtzal",
"bindgen",
"bleal",
"blez",
"blezal",
"bltal",
"bltz",
"bltzal",
"bnaal",
"bnan",
"bnaz",
"bnazal",
"bneal",
"bnez",
"bnezal",
"bools",
"brap",
"brapz",
"brdns",
"brdse",
"breq",
"breqz",
"brge",
"brgez",
"brgt",
"brgtz",
"brle",
"brlez",
"brlt",
"brltz",
"brna",
"brnan",
"brnaz",
"brne",
"brnez",
"Cannifier",
"cannister",
"Carrage",
"CHAC",
"Circuitboard",
"Clrd",
"codegen",
"columnated",
"composter",
"conv",
"Cryo",
"cstyle",
"Darga",
"Datalink",
"Depressurising",
"desync",
"dylib",
"endpos",
"Espaciais",
"Exgress",
"Faily",
"Fenoxitone",
"Frida",
"fromstr",
"getd",
"glowstick",
"glowy",
"Hardsuit",
"Harvie",
"hashables",
"Hastelloy",
"headcrabs",
"hstack",
"Huxi",
"Idents",
"iface",
"impls",
"Inconel",
"indexmap",
"inext",
"inextp",
"infile",
"Instructable",
"insts",
"intf",
"itertools",
"Jenk",
"jetpack",
"kbshortcutmenu",
"Keybind",
"Larre",
"lbns",
"logicable",
"LogicSlotType",
"logicslottypes",
"LogicSlotTypes",
"logictype",
"logictypes",
"lparen",
"lzma",
"Mineables",
"MKII",
"moondust",
"Mothership",
"mscorlib",
"MSEED",
"Murtons",
"ninf",
"Nitrice",
"nomatch",
"nops",
"Norsec",
"offworld",
"Omni",
"onsreen",
"oprs",
"overcolumn",
"Overlength",
"Padi",
"parentable",
"pedia",
"peekable",
"prec",
"preproc",
"Pressurising",
"prettyplease",
"PRNG",
"putd",
"QUICKFIX",
"reagentmode",
"reagentmodes",
"reborrow",
"Recurso",
"repr",
"Respawn",
"retval",
"Rmap",
"rocketstation",
"rparen",
"sapz",
"sattellite",
"sdns",
"sdse",
"searchbox",
"searchbtn",
"seqz",
"serde",
"settingsmenu",
"sgez",
"sgtz",
"Sinotai",
"slez",
"slotlogic",
"slotlogicable",
"slottype",
"sltz",
"snan",
"snanz",
"snaz",
"snez",
"spacepack",
"spalling",
"splitn",
"stablizer",
"Starck",
"Stationeer",
"Stationeers",
"stationpedia",
"stdweb",
"stopo",
"Stuppen",
"superalloys",
"tbody",
"tepratures",
"thiserror",
"tokentype",
"toolbelt",
"Topo",
"trunc",
"Tsify",
"undarkens",
"uneval",
"unparse",
"unpowered",
"WASD",
"whos",
"Wirecutters",
"Xigo",
"xtask",
"Zoomer",
"Zrilian"
]
}

View File

@@ -0,0 +1,12 @@
{
"bapz": "Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)",
"bapzal": "Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8) and store next line number in ra",
"bnaz": "Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8)",
"bnazal": "Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8) and store next line number in ra",
"brapz": "Relative branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)",
"brnaz": "Relative branch to line c if abs(a) > max(b * abs(a), float.epsilon * 8)",
"sapz": "Register = 1 if abs(a) <= max(b * abs(a), float.epsilon * 8), otherwise 0",
"snaz": "Register = 1 if abs(a) > max(b * abs(a), float.epsilon), otherwise 0",
"log": "Register = base e log(a) or ln(a)",
"exp": "Register = exp(a) or e^a"
}

View File

@@ -10,33 +10,53 @@ crate-type = ["lib", "cdylib"]
[dependencies]
stationeers_data = { path = "../stationeers_data" }
const-crc32 = "1.3.0"
itertools = "0.12.1"
phf = "0.11.2"
itertools = "0.13.0"
macro_rules_attribute = "0.2.0"
paste = "1.0.15"
phf = { version = "0.11.2", features = ["macros"] }
rand = "0.8.5"
regex = "1.10.3"
serde = { version = "1.0.197", features = ["derive"] }
serde_with = "3.7.0"
regex = "1.10.4"
serde = "1.0.202"
serde_derive = "1.0.202"
serde_with = "3.8.1"
strum = { version = "0.26.2", features = ["derive", "phf", "strum_macros"] }
strum_macros = "0.26.2"
thiserror = "1.0.58"
time = { version = "0.3.34", features = [
thiserror = "1.0.61"
time = { version = "0.3.36", features = [
"formatting",
"parsing",
"serde",
"local-offset",
] }
tsify = { version = "0.4.5", optional = true, features = ["js"] }
wasm-bindgen = { version = "0.2.92", optional = true }
tracing = "0.1.40"
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
time = { version = "0.3.34", features = [
time = { version = "0.3.36", features = [
"formatting",
"serde",
"local-offset",
"wasm-bindgen",
] }
[dev-dependencies]
color-eyre = "0.6.3"
serde_json = "1.0.117"
[build-dependencies]
convert_case = "0.6.0"
phf_codegen = "0.11.2"
regex = "1.10.3"
# Self dev dependency to enable prefab_database feature for tests
# ic10emu = { path = ".", features = ["prefab_database"] }
[features]
default = []
tsify = ["dep:tsify", "dep:wasm-bindgen", "stationeers_data/tsify"]
prefab_database = [
"stationeers_data/prefab_database",
] # compile with the prefab database enabled
reagent_database = [
"stationeers_data/reagent_database",
] # compile with the prefab database enabled

View File

@@ -1,392 +0,0 @@
use convert_case::{Case, Casing};
use std::{
collections::BTreeSet,
env,
fmt::Display,
fs::{self, File},
io::{BufWriter, Write},
path::Path,
str::FromStr,
};
struct EnumVariant<P>
where
P: Display + FromStr,
{
pub aliases: Vec<String>,
pub value: Option<P>,
pub deprecated: bool,
}
fn write_repr_enum<'a, T: std::io::Write, I, P>(
writer: &mut BufWriter<T>,
name: &str,
variants: I,
use_phf: bool,
) where
P: Display + FromStr + 'a,
I: IntoIterator<Item = &'a (String, EnumVariant<P>)>,
{
let additional_strum = if use_phf { "#[strum(use_phf)]\n" } else { "" };
write!(
writer,
"#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumString, AsRefStr, EnumProperty, EnumIter, Serialize, Deserialize)]\n\
{additional_strum}\
pub enum {name} {{\n"
)
.unwrap();
for (name, variant) in variants {
let variant_name = name.replace('.', "").to_case(Case::Pascal);
let mut serialize = vec![name.clone()];
serialize.extend(variant.aliases.iter().cloned());
let serialize_str = serialize
.into_iter()
.map(|s| format!("serialize = \"{s}\""))
.collect::<Vec<String>>()
.join(", ");
let mut props = Vec::new();
if variant.deprecated {
props.push("deprecated = \"true\"".to_owned());
}
if let Some(val) = &variant.value {
props.push(format!("value = \"{val}\""));
}
let props_str = if !props.is_empty() {
format!(", props( {} )", props.join(", "))
} else {
"".to_owned()
};
writeln!(
writer,
" #[strum({serialize_str}{props_str})] {variant_name},"
)
.unwrap();
}
writeln!(writer, "}}").unwrap();
}
fn write_logictypes() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("logictypes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut logictypes: Vec<(String, EnumVariant<u16>)> = Vec::new();
let l_infile = Path::new("data/logictypes.txt");
let l_contents = fs::read_to_string(l_infile).unwrap();
for line in l_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u16> = val_str.parse().ok();
let docs = it.next();
let deprecated = docs
.map(|docs| docs.trim().to_uppercase() == "DEPRECATED")
.unwrap_or(false);
if let Some(val) = val {
if let Some((_other_name, variant)) = logictypes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
variant.deprecated = deprecated;
} else {
logictypes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
deprecated,
},
));
}
} else {
logictypes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
deprecated,
},
));
}
}
let mut slotlogictypes: Vec<(String, EnumVariant<u8>)> = Vec::new();
let sl_infile = Path::new("data/slotlogictypes.txt");
let sl_contents = fs::read_to_string(sl_infile).unwrap();
for line in sl_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
let docs = it.next();
let deprecated = docs
.map(|docs| docs.trim().to_uppercase() == "DEPRECATED")
.unwrap_or(false);
if let Some(val) = val {
if let Some((_other_name, variant)) = slotlogictypes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
variant.deprecated = deprecated;
} else {
slotlogictypes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
deprecated,
},
));
}
} else {
slotlogictypes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
deprecated,
},
));
}
}
write_repr_enum(&mut writer, "LogicType", &logictypes, true);
println!("cargo:rerun-if-changed=data/logictypes.txt");
write_repr_enum(&mut writer, "SlotLogicType", &slotlogictypes, true);
println!("cargo:rerun-if-changed=data/slotlogictypes.txt");
}
fn write_enums() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("enums.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut enums_map: Vec<(String, EnumVariant<u32>)> = Vec::new();
let e_infile = Path::new("data/enums.txt");
let e_contents = fs::read_to_string(e_infile).unwrap();
for line in e_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u32> = val_str.parse().ok();
let docs = it.next();
let deprecated = docs
.map(|docs| docs.trim().to_uppercase() == "DEPRECATED")
.unwrap_or(false);
enums_map.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
deprecated,
},
));
}
write_repr_enum(&mut writer, "LogicEnums", &enums_map, true);
println!("cargo:rerun-if-changed=data/enums.txt");
}
fn write_modes() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("modes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut batchmodes: Vec<(String, EnumVariant<u8>)> = Vec::new();
let b_infile = Path::new("data/batchmodes.txt");
let b_contents = fs::read_to_string(b_infile).unwrap();
for line in b_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
if let Some(val) = val {
if let Some((_other_name, variant)) = batchmodes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
} else {
batchmodes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
deprecated: false,
},
));
}
} else {
batchmodes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
deprecated: false,
},
));
}
}
let mut reagentmodes: Vec<(String, EnumVariant<u8>)> = Vec::new();
let r_infile = Path::new("data/reagentmodes.txt");
let r_contents = fs::read_to_string(r_infile).unwrap();
for line in r_contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
if let Some(val) = val {
if let Some((_other_name, variant)) = reagentmodes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
} else {
reagentmodes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
deprecated: false,
},
));
}
} else {
reagentmodes.push((
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
deprecated: false,
},
));
}
}
write_repr_enum(&mut writer, "BatchMode", &batchmodes, false);
println!("cargo:rerun-if-changed=data/batchmodes.txt");
write_repr_enum(&mut writer, "ReagentMode", &reagentmodes, false);
println!("cargo:rerun-if-changed=data/reagentmodes.txt");
}
fn write_constants() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("constants.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut constants_lookup_map_builder = ::phf_codegen::Map::new();
let infile = Path::new("data/constants.txt");
let contents = fs::read_to_string(infile).unwrap();
for line in contents.lines().filter(|l| !l.trim().is_empty()) {
let mut it = line.splitn(3, ' ');
let name = it.next().unwrap();
let constant = it.next().unwrap();
constants_lookup_map_builder.entry(name, constant);
}
writeln!(
&mut writer,
"#[allow(clippy::approx_constant)] pub(crate) const CONSTANTS_LOOKUP: phf::Map<&'static str, f64> = {};",
constants_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/constants.txt");
}
fn write_instructions_enum() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("instructions.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut instructions = BTreeSet::new();
let infile = Path::new("data/instructions.txt");
let contents = fs::read_to_string(infile).unwrap();
for line in contents.lines() {
let mut it = line.split(' ');
let instruction = it.next().unwrap();
instructions.insert(instruction.to_string());
}
write!(
&mut writer,
"#[derive(Debug, Display, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]\n\
pub enum InstructionOp {{\n\
"
)
.unwrap();
writeln!(&mut writer, " Nop,").unwrap();
for typ in &instructions {
writeln!(&mut writer, " {},", typ.to_case(Case::Pascal)).unwrap();
}
writeln!(&mut writer, "}}").unwrap();
write!(
&mut writer,
"impl FromStr for InstructionOp {{\n \
type Err = ParseError;\n \
fn from_str(s: &str) -> Result<Self, Self::Err> {{\n \
let end = s.len();\n \
match s {{\n"
)
.unwrap();
for typ in &instructions {
let name = typ.to_case(Case::Pascal);
writeln!(&mut writer, " \"{typ}\" => Ok(Self::{name}),").unwrap();
}
write!(
&mut writer,
" _ => Err(crate::grammar::ParseError {{ line: 0, start: 0, end, msg: format!(\"Unknown instruction '{{}}'\", s) }})\n \
}}\n \
}}\n\
}}"
)
.unwrap();
println!("cargo:rerun-if-changed=data/instructions.txt");
}
fn main() {
// write_instructions();
write_logictypes();
write_modes();
write_constants();
write_enums();
write_instructions_enum();
}

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +0,0 @@
Average 0 Average of all read values
Maximum 3 Highest of all read values
Minimum 2 Lowest of all read values
Sum 1 All read values added together

View File

@@ -1,7 +0,0 @@
nan f64::NAN A constant representing 'not a number'. This constants technically provides a 'quiet' NaN, a signal NaN from some instructions will result in an exception and halt execution
pinf f64::INFINITY A constant representing a positive infinite value
ninf f64::NEG_INFINITY A constant representing a negative infinite value
pi 3.141592653589793f64 \nA constant representing the ratio of the circumference of a circle to its diameter, provided in double percision
deg2rad 0.0174532923847437f64 \nDegrees to radians conversion constant
rad2deg 57.2957801818848f64 \nRadians to degrees conversion constant
epsilon f64::EPSILON A constant representing the smallest value representable in double precision

View File

@@ -1,455 +0,0 @@
AirCon.Cold 0
AirCon.Hot 1
AirControl.Draught 4
AirControl.None 0
AirControl.Offline 1
AirControl.Pressure 2
Color.Black 7 Black
Color.Blue 0 Blue
Color.Brown 8 Brown
Color.Gray 1 Gray
Color.Green 2 Green
Color.Khaki 9 Khaki
Color.Orange 3 Orange
Color.Pink 10 Pink
Color.Purple 11 Purple
Color.Red 4 Red
Color.White 6 White
Color.Yellow 5 Yellow
DaylightSensorMode.Default 0
DaylightSensorMode.Horizontal 1
DaylightSensorMode.Vertical 2
ElevatorMode.Downward 2
ElevatorMode.Stationary 0
ElevatorMode.Upward 1
EntityState.Alive 0
EntityState.Dead 1
EntityState.Decay 3
EntityState.Unconscious 2
Equals 0
GasType.CarbonDioxide 4
GasType.Hydrogen 16384
GasType.LiquidCarbonDioxide 2048
GasType.LiquidHydrogen 32768
GasType.LiquidNitrogen 128
GasType.LiquidNitrousOxide 8192
GasType.LiquidOxygen 256
GasType.LiquidPollutant 4096
GasType.LiquidVolatiles 512
GasType.Nitrogen 2
GasType.NitrousOxide 64
GasType.Oxygen 1
GasType.Pollutant 16
GasType.PollutedWater 65536
GasType.Steam 1024
GasType.Undefined 0
GasType.Volatiles 8
GasType.Water 32
Greater 1
Less 2
LogicSlotType.Charge 10 returns current energy charge the slot occupant is holding
LogicSlotType.ChargeRatio 11 returns current energy charge the slot occupant is holding as a ratio between 0 and 1 of its maximum
LogicSlotType.Class 12 returns integer representing the class of object
LogicSlotType.Damage 4 returns the damage state of the item in the slot
LogicSlotType.Efficiency 5 returns the growth efficiency of the plant in the slot
LogicSlotType.FilterType 25
LogicSlotType.Growth 7 returns the current growth state of the plant in the slot
LogicSlotType.Health 6 returns the health of the plant in the slot
LogicSlotType.LineNumber 19
LogicSlotType.Lock 23
LogicSlotType.Mature 16 returns 1 if the plant in this slot is mature, 0 when it isn't
LogicSlotType.MaxQuantity 15 returns the max stack size of the item in the slot
LogicSlotType.None 0 No description
LogicSlotType.OccupantHash 2 returns the has of the current occupant, the unique identifier of the thing
LogicSlotType.Occupied 1 returns 0 when slot is not occupied, 1 when it is
LogicSlotType.On 22
LogicSlotType.Open 21
LogicSlotType.PrefabHash 17 returns the hash of the structure in the slot
LogicSlotType.Pressure 8 returns pressure of the slot occupants internal atmosphere
LogicSlotType.PressureAir 14 returns pressure in the air tank of the jetpack in this slot
LogicSlotType.PressureWaste 13 returns pressure in the waste tank of the jetpack in this slot
LogicSlotType.Quantity 3 returns the current quantity, such as stack size, of the item in the slot
LogicSlotType.ReferenceId 26
LogicSlotType.Seeding 18 Whether a plant is seeding (ready to harvest seeds from). Returns 1 if seeding or 0 if not.
LogicSlotType.SortingClass 24
LogicSlotType.Temperature 9 returns temperature of the slot occupants internal atmosphere
LogicSlotType.Volume 20
LogicType.Acceleration 216 Change in velocity. Rockets that are deccelerating when landing will show this as negative value.
LogicType.Activate 9 1 if device is activated (usually means running), otherwise 0
LogicType.AirRelease 75 The current state of the air release system, for example AirRelease = 1 for a Hardsuit sets Air Release to On
LogicType.AlignmentError 243
LogicType.Apex 238
LogicType.AutoLand 226 Engages the automatic landing algorithm. The rocket will automatically throttle and turn on and off its engines to achieve a smooth landing.
LogicType.AutoShutOff 218 Turns off all devices in the rocket upon reaching destination
LogicType.Bpm 103 Bpm
LogicType.BurnTimeRemaining 225 Estimated time in seconds until fuel is depleted. Calculated based on current fuel usage.
LogicType.CelestialHash 242
LogicType.CelestialParentHash 250
LogicType.Channel0 165
LogicType.Channel1 166
LogicType.Channel2 167
LogicType.Channel3 168
LogicType.Channel4 169
LogicType.Channel5 170
LogicType.Channel6 171
LogicType.Channel7 172
LogicType.Charge 11 The current charge the device has
LogicType.Chart 256
LogicType.ChartedNavPoints 259
LogicType.ClearMemory 62 When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when actioned
LogicType.CollectableGoods 101
LogicType.Color 38 \n Whether driven by concerns for clarity, safety or simple aesthetics, {LINK:Stationeers;Stationeers} have access to a small rainbow of colors for their constructions. These are the color setting for devices, represented as an integer.\n\n0: Blue\n1: Grey\n2: Green\n3: Orange\n4: Red\n5: Yellow\n6: White\n7: Black\n8: Brown\n9: Khaki\n10: Pink\n11: Purple\n\n It is an unwavering universal law that anything higher than 11 will be purple. The {LINK:ODA;ODA} is powerless to change this. Similarly, anything lower than 0 will be Blue.\n
LogicType.Combustion 98 The assess atmosphere is on fire. Returns 1 if atmosphere is on fire, 0 if not.
LogicType.CombustionInput 146 The assess atmosphere is on fire. Returns 1 if device's input network is on fire, 0 if not.
LogicType.CombustionInput2 147 The assess atmosphere is on fire. Returns 1 if device's Input2 network is on fire, 0 if not.
LogicType.CombustionLimiter 153 Retards the rate of combustion inside the machine (range: 0-100), with 0 being the slowest rate of combustion and 100 being the fastest
LogicType.CombustionOutput 148 The assess atmosphere is on fire. Returns 1 if device's Output network is on fire, 0 if not.
LogicType.CombustionOutput2 149 The assess atmosphere is on fire. Returns 1 if device's Output2 network is on fire, 0 if not.
LogicType.CompletionRatio 61 How complete the current production is for this device, between 0 and 1
LogicType.ContactTypeId 198 The type id of the contact.
LogicType.CurrentCode 261
LogicType.CurrentResearchPodType 93
LogicType.Density 262
LogicType.DestinationCode 215 Unique identifier code for a destination on the space map.
LogicType.Discover 255
LogicType.DistanceAu 244
LogicType.DistanceKm 249
LogicType.DrillCondition 240
LogicType.DryMass 220 The Mass in kilograms of the rocket excluding fuel. The more massive the rocket the more fuel will be required to move to a new location in space.
LogicType.Eccentricity 247
LogicType.ElevatorLevel 40 Level the elevator is currently at
LogicType.ElevatorSpeed 39 Current speed of the elevator
LogicType.EntityState 239
LogicType.EnvironmentEfficiency 104 The Environment Efficiency reported by the machine, as a float between 0 and 1
LogicType.Error 4 1 if device is in error state, otherwise 0
LogicType.ExhaustVelocity 235
LogicType.ExportCount 63 How many items exported since last ClearMemory
LogicType.ExportQuantity 31 Total quantity of items exported by the device
LogicType.ExportSlotHash 42 DEPRECATED
LogicType.ExportSlotOccupant 32 DEPRECATED
LogicType.Filtration 74 The current state of the filtration system, for example Filtration = 1 for a Hardsuit sets filtration to On
LogicType.FlightControlRule 236
LogicType.Flush 174 Set to 1 to activate the flush function on the device
LogicType.ForceWrite 85 Forces Logic Writer devices to rewrite value
LogicType.ForwardX 227
LogicType.ForwardY 228
LogicType.ForwardZ 229
LogicType.Fuel 99
LogicType.Harvest 69 Performs the harvesting action for any plant based machinery
LogicType.Horizontal 20 Horizontal setting of the device
LogicType.HorizontalRatio 34 Radio of horizontal setting for device
LogicType.Idle 37 Returns 1 if the device is currently idle, otherwise 0
LogicType.ImportCount 64 How many items imported since last ClearMemory
LogicType.ImportQuantity 29 Total quantity of items imported by the device
LogicType.ImportSlotHash 43 DEPRECATED
LogicType.ImportSlotOccupant 30 DEPRECATED
LogicType.Inclination 246
LogicType.Index 241
LogicType.InterrogationProgress 157 Progress of this sattellite dish's interrogation of its current target, as a ratio from 0-1
LogicType.LineNumber 173 The line number of current execution for an integrated circuit running on this device. While this number can be written, use with caution
LogicType.Lock 10 1 if device is locked, otherwise 0, can be set in most devices and prevents the user from access the values
LogicType.ManualResearchRequiredPod 94
LogicType.Mass 219 The total Mass of the rocket in kilograms including fuel and cargo. The more massive the rocket the more fuel will be required to move to a new location in space.
LogicType.Maximum 23 Maximum setting of the device
LogicType.MineablesInQueue 96
LogicType.MineablesInVicinity 95
LogicType.MinedQuantity 266
LogicType.MinimumWattsToContact 163
LogicType.Mode 3 Integer for mode state, different devices will have different mode states available to them
LogicType.NavPoints 258
LogicType.NextWeatherEventTime 97
LogicType.None 0 No description
LogicType.On 28 The current state of the device, 0 for off, 1 for on
LogicType.Open 2 1 if device is open, otherwise 0
LogicType.OperationalTemperatureEfficiency 150 How the input pipe's temperature effects the machines efficiency
LogicType.OrbitPeriod 245
LogicType.Orientation 230
LogicType.Output 70 The output operation for a sort handling device, such as a stacker or sorter, when in logic mode the device will only action one repetition when set zero or above and then back to -1 and await further instructions
LogicType.PassedMoles 234
LogicType.Plant 68 Performs the planting action for any plant based machinery
LogicType.PlantEfficiency1 52 DEPRECATED
LogicType.PlantEfficiency2 53 DEPRECATED
LogicType.PlantEfficiency3 54 DEPRECATED
LogicType.PlantEfficiency4 55 DEPRECATED
LogicType.PlantGrowth1 48 DEPRECATED
LogicType.PlantGrowth2 49 DEPRECATED
LogicType.PlantGrowth3 50 DEPRECATED
LogicType.PlantGrowth4 51 DEPRECATED
LogicType.PlantHash1 56 DEPRECATED
LogicType.PlantHash2 57 DEPRECATED
LogicType.PlantHash3 58 DEPRECATED
LogicType.PlantHash4 59 DEPRECATED
LogicType.PlantHealth1 44 DEPRECATED
LogicType.PlantHealth2 45 DEPRECATED
LogicType.PlantHealth3 46 DEPRECATED
LogicType.PlantHealth4 47 DEPRECATED
LogicType.PositionX 76 The current position in X dimension in world coordinates
LogicType.PositionY 77 The current position in Y dimension in world coordinates
LogicType.PositionZ 78 The current position in Z dimension in world coordinates
LogicType.Power 1 Can be read to return if the device is correctly powered or not, set via the power system, return 1 if powered and 0 if not
LogicType.PowerActual 26 How much energy the device or network is actually using
LogicType.PowerGeneration 65 Returns how much power is being generated
LogicType.PowerPotential 25 How much energy the device or network potentially provides
LogicType.PowerRequired 36 Power requested from the device and/or network
LogicType.PrefabHash 84 The hash of the structure
LogicType.Pressure 5 The current pressure reading of the device
LogicType.PressureEfficiency 152 How the pressure of the input pipe and waste pipe effect the machines efficiency
LogicType.PressureExternal 7 Setting for external pressure safety, in KPa
LogicType.PressureInput 106 The current pressure reading of the device's Input Network
LogicType.PressureInput2 116 The current pressure reading of the device's Input2 Network
LogicType.PressureInternal 8 Setting for internal pressure safety, in KPa
LogicType.PressureOutput 126 The current pressure reading of the device's Output Network
LogicType.PressureOutput2 136 The current pressure reading of the device's Output2 Network
LogicType.PressureSetting 71 The current setting for the internal pressure of the object (e.g. the Hardsuit Air release), in KPa
LogicType.Progress 214 Progress of the rocket to the next node on the map expressed as a value between 0-1.
LogicType.Quantity 27 Total quantity on the device
LogicType.Ratio 24 Context specific value depending on device, 0 to 1 based ratio
LogicType.RatioCarbonDioxide 15 The ratio of {GAS:CarbonDioxide} in device atmosphere
LogicType.RatioCarbonDioxideInput 109 The ratio of {GAS:CarbonDioxide} in device's input network
LogicType.RatioCarbonDioxideInput2 119 The ratio of {GAS:CarbonDioxide} in device's Input2 network
LogicType.RatioCarbonDioxideOutput 129 The ratio of {GAS:CarbonDioxide} in device's Output network
LogicType.RatioCarbonDioxideOutput2 139 The ratio of {GAS:CarbonDioxide} in device's Output2 network
LogicType.RatioHydrogen 252 The ratio of {GAS:Hydrogen} in device's Atmopshere
LogicType.RatioLiquidCarbonDioxide 199 The ratio of {GAS:LiquidCarbonDioxide} in device's Atmosphere
LogicType.RatioLiquidCarbonDioxideInput 200 The ratio of {GAS:LiquidCarbonDioxide} in device's Input Atmosphere
LogicType.RatioLiquidCarbonDioxideInput2 201 The ratio of {GAS:LiquidCarbonDioxide} in device's Input2 Atmosphere
LogicType.RatioLiquidCarbonDioxideOutput 202 The ratio of {GAS:LiquidCarbonDioxide} in device's device's Output Atmosphere
LogicType.RatioLiquidCarbonDioxideOutput2 203 The ratio of {GAS:LiquidCarbonDioxide} in device's Output2 Atmopshere
LogicType.RatioLiquidHydrogen 253 The ratio of {GAS:LiquidHydrogen} in device's Atmopshere
LogicType.RatioLiquidNitrogen 177 The ratio of {GAS:LiquidNitrogen} in device atmosphere
LogicType.RatioLiquidNitrogenInput 178 The ratio of {GAS:LiquidNitrogen} in device's input network
LogicType.RatioLiquidNitrogenInput2 179 The ratio of {GAS:LiquidNitrogen} in device's Input2 network
LogicType.RatioLiquidNitrogenOutput 180 The ratio of {GAS:LiquidNitrogen} in device's Output network
LogicType.RatioLiquidNitrogenOutput2 181 The ratio of {GAS:LiquidNitrogen} in device's Output2 network
LogicType.RatioLiquidNitrousOxide 209 The ratio of {GAS:LiquidNitrousOxide} in device's Atmosphere
LogicType.RatioLiquidNitrousOxideInput 210 The ratio of {GAS:LiquidNitrousOxide} in device's Input Atmosphere
LogicType.RatioLiquidNitrousOxideInput2 211 The ratio of {GAS:LiquidNitrousOxide} in device's Input2 Atmosphere
LogicType.RatioLiquidNitrousOxideOutput 212 The ratio of {GAS:LiquidNitrousOxide} in device's device's Output Atmosphere
LogicType.RatioLiquidNitrousOxideOutput2 213 The ratio of {GAS:LiquidNitrousOxide} in device's Output2 Atmopshere
LogicType.RatioLiquidOxygen 183 The ratio of {GAS:LiquidOxygen} in device's Atmosphere
LogicType.RatioLiquidOxygenInput 184 The ratio of {GAS:LiquidOxygen} in device's Input Atmosphere
LogicType.RatioLiquidOxygenInput2 185 The ratio of {GAS:LiquidOxygen} in device's Input2 Atmosphere
LogicType.RatioLiquidOxygenOutput 186 The ratio of {GAS:LiquidOxygen} in device's device's Output Atmosphere
LogicType.RatioLiquidOxygenOutput2 187 The ratio of {GAS:LiquidOxygen} in device's Output2 Atmopshere
LogicType.RatioLiquidPollutant 204 The ratio of {GAS:LiquidPollutant} in device's Atmosphere
LogicType.RatioLiquidPollutantInput 205 The ratio of {GAS:LiquidPollutant} in device's Input Atmosphere
LogicType.RatioLiquidPollutantInput2 206 The ratio of {GAS:LiquidPollutant} in device's Input2 Atmosphere
LogicType.RatioLiquidPollutantOutput 207 The ratio of {GAS:LiquidPollutant} in device's device's Output Atmosphere
LogicType.RatioLiquidPollutantOutput2 208 The ratio of {GAS:LiquidPollutant} in device's Output2 Atmopshere
LogicType.RatioLiquidVolatiles 188 The ratio of {GAS:LiquidVolatiles} in device's Atmosphere
LogicType.RatioLiquidVolatilesInput 189 The ratio of {GAS:LiquidVolatiles} in device's Input Atmosphere
LogicType.RatioLiquidVolatilesInput2 190 The ratio of {GAS:LiquidVolatiles} in device's Input2 Atmosphere
LogicType.RatioLiquidVolatilesOutput 191 The ratio of {GAS:LiquidVolatiles} in device's device's Output Atmosphere
LogicType.RatioLiquidVolatilesOutput2 192 The ratio of {GAS:LiquidVolatiles} in device's Output2 Atmopshere
LogicType.RatioNitrogen 16 The ratio of nitrogen in device atmosphere
LogicType.RatioNitrogenInput 110 The ratio of nitrogen in device's input network
LogicType.RatioNitrogenInput2 120 The ratio of nitrogen in device's Input2 network
LogicType.RatioNitrogenOutput 130 The ratio of nitrogen in device's Output network
LogicType.RatioNitrogenOutput2 140 The ratio of nitrogen in device's Output2 network
LogicType.RatioNitrousOxide 83 The ratio of {GAS:NitrousOxide} in device atmosphere
LogicType.RatioNitrousOxideInput 114 The ratio of {GAS:NitrousOxide} in device's input network
LogicType.RatioNitrousOxideInput2 124 The ratio of {GAS:NitrousOxide} in device's Input2 network
LogicType.RatioNitrousOxideOutput 134 The ratio of {GAS:NitrousOxide} in device's Output network
LogicType.RatioNitrousOxideOutput2 144 The ratio of {GAS:NitrousOxide} in device's Output2 network
LogicType.RatioOxygen 14 The ratio of oxygen in device atmosphere
LogicType.RatioOxygenInput 108 The ratio of oxygen in device's input network
LogicType.RatioOxygenInput2 118 The ratio of oxygen in device's Input2 network
LogicType.RatioOxygenOutput 128 The ratio of oxygen in device's Output network
LogicType.RatioOxygenOutput2 138 The ratio of oxygen in device's Output2 network
LogicType.RatioPollutant 17 The ratio of pollutant in device atmosphere
LogicType.RatioPollutantInput 111 The ratio of pollutant in device's input network
LogicType.RatioPollutantInput2 121 The ratio of pollutant in device's Input2 network
LogicType.RatioPollutantOutput 131 The ratio of pollutant in device's Output network
LogicType.RatioPollutantOutput2 141 The ratio of pollutant in device's Output2 network
LogicType.RatioPollutedWater 254 The ratio of polluted water in device atmosphere
LogicType.RatioSteam 193 The ratio of {GAS:Steam} in device's Atmosphere
LogicType.RatioSteamInput 194 The ratio of {GAS:Steam} in device's Input Atmosphere
LogicType.RatioSteamInput2 195 The ratio of {GAS:Steam} in device's Input2 Atmosphere
LogicType.RatioSteamOutput 196 The ratio of {GAS:Steam} in device's device's Output Atmosphere
LogicType.RatioSteamOutput2 197 The ratio of {GAS:Steam} in device's Output2 Atmopshere
LogicType.RatioVolatiles 18 The ratio of volatiles in device atmosphere
LogicType.RatioVolatilesInput 112 The ratio of volatiles in device's input network
LogicType.RatioVolatilesInput2 122 The ratio of volatiles in device's Input2 network
LogicType.RatioVolatilesOutput 132 The ratio of volatiles in device's Output network
LogicType.RatioVolatilesOutput2 142 The ratio of volatiles in device's Output2 network
LogicType.RatioWater 19 The ratio of water in device atmosphere
LogicType.RatioWaterInput 113 The ratio of water in device's input network
LogicType.RatioWaterInput2 123 The ratio of water in device's Input2 network
LogicType.RatioWaterOutput 133 The ratio of water in device's Output network
LogicType.RatioWaterOutput2 143 The ratio of water in device's Output2 network
LogicType.ReEntryAltitude 237
LogicType.Reagents 13 Total number of reagents recorded by the device
LogicType.RecipeHash 41 Current hash of the recipe the device is set to produce
LogicType.ReferenceId 217
LogicType.RequestHash 60 When set to the unique identifier, requests an item of the provided type from the device
LogicType.RequiredPower 33 Idle operating power quantity, does not necessarily include extra demand power
LogicType.ReturnFuelCost 100
LogicType.Richness 263
LogicType.Rpm 155 The number of revolutions per minute that the device's spinning mechanism is doing
LogicType.SemiMajorAxis 248
LogicType.Setting 12 A variable setting that can be read or written, depending on the device
LogicType.SettingInput 91
LogicType.SettingOutput 92
LogicType.SignalID 87 Returns the contact ID of the strongest signal from this Satellite
LogicType.SignalStrength 86 Returns the degree offset of the strongest contact
LogicType.Sites 260
LogicType.Size 264
LogicType.SizeX 160 Size on the X (right) axis of the object in largeGrids (a largeGrid is 2meters)
LogicType.SizeY 161 Size on the Y(Up) axis of the object in largeGrids (a largeGrid is 2meters)
LogicType.SizeZ 162 Size on the Z(Forward) axis of the object in largeGrids (a largeGrid is 2meters)
LogicType.SolarAngle 22 Solar angle of the device
LogicType.SolarIrradiance 176
LogicType.SoundAlert 175 Plays a sound alert on the devices speaker
LogicType.Stress 156 Machines get stressed when working hard. When Stress reaches 100 the machine will automatically shut down
LogicType.Survey 257
LogicType.TargetPadIndex 158 The index of the trader landing pad on this devices data network that it will try to call a trader in to land
LogicType.TargetX 88 The target position in X dimension in world coordinates
LogicType.TargetY 89 The target position in Y dimension in world coordinates
LogicType.TargetZ 90 The target position in Z dimension in world coordinates
LogicType.Temperature 6 The current temperature reading of the device
LogicType.TemperatureDifferentialEfficiency 151 How the difference between the input pipe and waste pipe temperatures effect the machines efficiency
LogicType.TemperatureExternal 73 The temperature of the outside of the device, usually the world atmosphere surrounding it
LogicType.TemperatureInput 107 The current temperature reading of the device's Input Network
LogicType.TemperatureInput2 117 The current temperature reading of the device's Input2 Network
LogicType.TemperatureOutput 127 The current temperature reading of the device's Output Network
LogicType.TemperatureOutput2 137 The current temperature reading of the device's Output2 Network
LogicType.TemperatureSetting 72 The current setting for the internal temperature of the object (e.g. the Hardsuit A/C)
LogicType.Throttle 154 Increases the rate at which the machine works (range: 0-100)
LogicType.Thrust 221 Total current thrust of all rocket engines on the rocket in Newtons.
LogicType.ThrustToWeight 223 Ratio of thrust to weight of rocket. Weight is effected by local body gravity. A rocket with a low thrust to weight will expend more fuel during launch and landing.
LogicType.Time 102 Time
LogicType.TimeToDestination 224 Estimated time in seconds until rocket arrives at target destination.
LogicType.TotalMoles 66 Returns the total moles of the device
LogicType.TotalMolesInput 115 Returns the total moles of the device's Input Network
LogicType.TotalMolesInput2 125 Returns the total moles of the device's Input2 Network
LogicType.TotalMolesOutput 135 Returns the total moles of the device's Output Network
LogicType.TotalMolesOutput2 145 Returns the total moles of the device's Output2 Network
LogicType.TotalQuantity 265
LogicType.TrueAnomaly 251
LogicType.VelocityMagnitude 79 The current magnitude of the velocity vector
LogicType.VelocityRelativeX 80 The current velocity X relative to the forward vector of this
LogicType.VelocityRelativeY 81 The current velocity Y relative to the forward vector of this
LogicType.VelocityRelativeZ 82 The current velocity Z relative to the forward vector of this
LogicType.VelocityX 231
LogicType.VelocityY 232
LogicType.VelocityZ 233
LogicType.Vertical 21 Vertical setting of the device
LogicType.VerticalRatio 35 Radio of vertical setting for device
LogicType.Volume 67 Returns the device atmosphere volume
LogicType.VolumeOfLiquid 182 The total volume of all liquids in Liters in the atmosphere
LogicType.WattsReachingContact 164 The amount of watts actually hitting the contact. This is effected by the power of the dish and how far off-axis the dish is from the contact vector
LogicType.Weight 222 Weight of Rocket in Newtons (Including fuel and cargo). Weight is effected by local body gravity.
LogicType.WorkingGasEfficiency 105 The Working Gas Efficiency reported by the machine, as a float between 0 and 1
NotEquals 3
PowerMode.Charged 4
PowerMode.Charging 3
PowerMode.Discharged 1
PowerMode.Discharging 2
PowerMode.Idle 0
RobotMode.Follow 1
RobotMode.MoveToTarget 2
RobotMode.None 0
RobotMode.PathToTarget 5
RobotMode.Roam 3
RobotMode.StorageFull 6
RobotMode.Unload 4
SlotClass.AccessCard 22
SlotClass.Appliance 18
SlotClass.Back 3
SlotClass.Battery 14
SlotClass.Belt 16
SlotClass.Blocked 38
SlotClass.Bottle 25
SlotClass.Cartridge 21
SlotClass.Circuit 24
SlotClass.Circuitboard 7
SlotClass.CreditCard 28
SlotClass.DataDisk 8
SlotClass.DirtCanister 29
SlotClass.DrillHead 35
SlotClass.Egg 15
SlotClass.Entity 13
SlotClass.Flare 37
SlotClass.GasCanister 5
SlotClass.GasFilter 4
SlotClass.Glasses 27
SlotClass.Helmet 1
SlotClass.Ingot 19
SlotClass.LiquidBottle 32
SlotClass.LiquidCanister 31
SlotClass.Magazine 23
SlotClass.Motherboard 6
SlotClass.None 0
SlotClass.Ore 10
SlotClass.Organ 9
SlotClass.Plant 11
SlotClass.ProgrammableChip 26
SlotClass.ScanningHead 36
SlotClass.SensorProcessingUnit 30
SlotClass.SoundCartridge 34
SlotClass.Suit 2
SlotClass.Tool 17
SlotClass.Torpedo 20
SlotClass.Uniform 12
SlotClass.Wreckage 33
SortingClass.Appliances 6
SortingClass.Atmospherics 7
SortingClass.Clothing 5
SortingClass.Default 0
SortingClass.Food 4
SortingClass.Ices 10
SortingClass.Kits 1
SortingClass.Ores 9
SortingClass.Resources 3
SortingClass.Storage 8
SortingClass.Tools 2
Sound.AirlockCycling 22
Sound.Alarm1 45
Sound.Alarm10 12
Sound.Alarm11 13
Sound.Alarm12 14
Sound.Alarm2 1
Sound.Alarm3 2
Sound.Alarm4 3
Sound.Alarm5 4
Sound.Alarm6 5
Sound.Alarm7 6
Sound.Alarm8 10
Sound.Alarm9 11
Sound.Alert 17
Sound.Danger 15
Sound.Depressurising 20
Sound.FireFireFire 28
Sound.Five 33
Sound.Floor 34
Sound.Four 32
Sound.HaltWhoGoesThere 27
Sound.HighCarbonDioxide 44
Sound.IntruderAlert 19
Sound.LiftOff 36
Sound.MalfunctionDetected 26
Sound.Music1 7
Sound.Music2 8
Sound.Music3 9
Sound.None 0
Sound.One 29
Sound.PollutantsDetected 43
Sound.PowerLow 23
Sound.PressureHigh 39
Sound.PressureLow 40
Sound.Pressurising 21
Sound.RocketLaunching 35
Sound.StormIncoming 18
Sound.SystemFailure 24
Sound.TemperatureHigh 41
Sound.TemperatureLow 42
Sound.Three 31
Sound.TraderIncoming 37
Sound.TraderLanded 38
Sound.Two 30
Sound.Warning 16
Sound.Welcome 25
TransmitterMode.Active 1
TransmitterMode.Passive 0
Vent.Inward 1
Vent.Outward 0

View File

@@ -1,14 +0,0 @@
{
"english": {
"bapz": "Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)",
"bapzal": "Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8) and store next line number in ra",
"bnaz": "Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8)",
"bnazal": "Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8) and store next line number in ra",
"brapz": "Relative branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)",
"brnaz": "Relative branch to line c if abs(a) > max(b * abs(a), float.epsilon * 8)",
"sapz": "Register = 1 if abs(a) <= max(b * abs(a), float.epsilon * 8), otherwise 0",
"snaz": "Register = 1 if abs(a) > max(b * abs(a), float.epsilon), otherwise 0",
"log": "Register = base e log(a) or ln(a)",
"exp": "Register = exp(a) or e^a"
}
}

View File

@@ -1,140 +0,0 @@
abs REGISTER VALUE
acos REGISTER VALUE
add REGISTER VALUE VALUE
alias NAME REGISTER_DEVICE
and REGISTER VALUE VALUE
asin REGISTER VALUE
atan REGISTER VALUE
atan2 REGISTER VALUE VALUE
bap VALUE VALUE VALUE VALUE
bapal VALUE VALUE VALUE VALUE
bapz VALUE VALUE VALUE
bapzal VALUE VALUE VALUE
bdns DEVICE VALUE
bdnsal DEVICE VALUE
bdse DEVICE VALUE
bdseal DEVICE VALUE
beq VALUE VALUE VALUE
beqal VALUE VALUE VALUE
beqz VALUE VALUE
beqzal VALUE VALUE
bge VALUE VALUE VALUE
bgeal VALUE VALUE VALUE
bgez VALUE VALUE
bgezal VALUE VALUE
bgt VALUE VALUE VALUE
bgtal VALUE VALUE VALUE
bgtz VALUE VALUE
bgtzal VALUE VALUE
ble VALUE VALUE VALUE
bleal VALUE VALUE VALUE
blez VALUE VALUE
blezal VALUE VALUE
blt VALUE VALUE VALUE
bltal VALUE VALUE VALUE
bltz VALUE VALUE
bltzal VALUE VALUE
bna VALUE VALUE VALUE VALUE
bnaal VALUE VALUE VALUE VALUE
bnan VALUE VALUE
bnaz VALUE VALUE VALUE
bnazal VALUE VALUE VALUE
bne VALUE VALUE VALUE
bneal VALUE VALUE VALUE
bnez VALUE VALUE
bnezal VALUE VALUE
brap VALUE VALUE VALUE VALUE
brapz VALUE VALUE VALUE
brdns DEVICE VALUE
brdse DEVICE VALUE
breq VALUE VALUE VALUE
breqz VALUE VALUE
brge VALUE VALUE VALUE
brgez VALUE VALUE
brgt VALUE VALUE VALUE
brgtz VALUE VALUE
brle VALUE VALUE VALUE
brlez VALUE VALUE
brlt VALUE VALUE VALUE
brltz VALUE VALUE
brna VALUE VALUE VALUE VALUE
brnan VALUE VALUE
brnaz VALUE VALUE VALUE
brne VALUE VALUE VALUE
brnez VALUE VALUE
ceil REGISTER VALUE
cos REGISTER VALUE
define NAME NUMBER
div REGISTER VALUE VALUE
exp REGISTER VALUE
floor REGISTER VALUE
get REGISTER DEVICE ADDRESS
getd REGISTER DEVICE_ID ADDRESS
hcf
j VALUE
jal VALUE
jr VALUE
l REGISTER DEVICE LOGIC_TYPE
lb REGISTER DEVICE_TYPE LOGIC_TYPE BATCH_MODE
lbn REGISTER DEVICE_TYPE DEVICE_NAME LOGIC_TYPE BATCH_MODE
lbns REGISTER DEVICE_TYPE DEVICE_NAME INDEX SLOT_LOGIC_TYPE BATCH_MODE
lbs REGISTER DEVICE_TYPE INDEX SLOT_LOGIC_TYPE BATCH_MODE
ld REGISTER DEVICE_ID LOGIC_TYPE
log REGISTER VALUE
lr REGISTER DEVICE REAGENT_MODE VALUE
ls REGISTER DEVICE VALUE SLOT_LOGIC_TYPE
max REGISTER VALUE VALUE
min REGISTER VALUE VALUE
mod REGISTER VALUE VALUE
move REGISTER VALUE
mul REGISTER VALUE VALUE
nor REGISTER VALUE VALUE
not REGISTER VALUE
or REGISTER VALUE VALUE
peek REGISTER
poke ADDRESS VALUE
pop REGISTER
push VALUE
put DEVICE ADDRESS VALUE
putd DEVICE_ID ADDRESS VALUE
rand REGISTER
round REGISTER VALUE
s DEVICE LOGIC_TYPE VALUE
sap REGISTER VALUE VALUE VALUE
sapz REGISTER VALUE VALUE
sb DEVICE_TYPE LOGIC_TYPE VALUE
sbn DEVICE_TYPE DEVICE_NAME LOGIC_TYPE VALUE
sbs DEVICE_TYPE INDEX SLOT_LOGIC_TYPE VALUE
sd DEVICE_ID LOGIC_TYPE VALUE
sdns REGISTER DEVICE
sdse REGISTER DEVICE
select REGISTER VALUE VALUE VALUE
seq REGISTER VALUE VALUE
seqz REGISTER VALUE
sge REGISTER VALUE VALUE
sgez REGISTER VALUE
sgt REGISTER VALUE VALUE
sgtz REGISTER VALUE
sin REGISTER VALUE
sla REGISTER VALUE VALUE
sle REGISTER VALUE VALUE
sleep VALUE
slez REGISTER VALUE
sll REGISTER VALUE VALUE
slt REGISTER VALUE VALUE
sltz REGISTER VALUE
sna REGISTER VALUE VALUE VALUE
snan REGISTER VALUE
snanz REGISTER VALUE
snaz REGISTER VALUE VALUE
sne REGISTER VALUE VALUE
snez REGISTER VALUE
sqrt REGISTER VALUE
sra REGISTER VALUE VALUE
srl REGISTER VALUE VALUE
ss DEVICE VALUE SLOT_LOGIC_TYPE REGISTER
sub REGISTER VALUE VALUE
tan REGISTER VALUE
trunc REGISTER VALUE
xor REGISTER VALUE VALUE
yield

View File

@@ -1,136 +0,0 @@
abs Register = the absolute value of a
acos Returns the angle (radians) whos cosine is the specified value
add Register = a + b.
alias Labels register or device reference with name, device references also affect what shows on the screws on the IC base.
and Performs a bitwise logical AND operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If both bits are 1, the resulting bit is set to 1. Otherwise the resulting bit is set to 0.
asin Returns the angle (radians) whos sine is the specified value
atan Returns the angle (radians) whos tan is the specified value
atan2 Returns the angle (radians) whose tangent is the quotient of two specified values: a (y) and b (x)
bap Branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8)
bapal Branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8) and store next line number in ra
bapz Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)
bapzal Branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8) and store next line number in ra
bdns Branch to line a if device d isn't set
bdnsal Jump execution to line a and store next line number if device is not set
bdse Branch to line a if device d is set
bdseal Jump execution to line a and store next line number if device is set
beq Branch to line c if a == b
beqal Branch to line c if a == b and store next line number in ra
beqz Branch to line b if a == 0
beqzal Branch to line b if a == 0 and store next line number in ra
bge Branch to line c if a >= b
bgeal Branch to line c if a >= b and store next line number in ra
bgez Branch to line b if a >= 0
bgezal Branch to line b if a >= 0 and store next line number in ra
bgt Branch to line c if a > b
bgtal Branch to line c if a > b and store next line number in ra
bgtz Branch to line b if a > 0
bgtzal Branch to line b if a > 0 and store next line number in ra
ble Branch to line c if a <= b
bleal Branch to line c if a <= b and store next line number in ra
blez Branch to line b if a <= 0
blezal Branch to line b if a <= 0 and store next line number in ra
blt Branch to line c if a < b
bltal Branch to line c if a < b and store next line number in ra
bltz Branch to line b if a < 0
bltzal Branch to line b if a < 0 and store next line number in ra
bna Branch to line d if abs(a - b) > max(c * max(abs(a), abs(b)), float.epsilon * 8)
bnaal Branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8) and store next line number in ra
bnan Branch to line b if a is not a number (NaN)
bnaz Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8)
bnazal Branch to line c if abs(a) > max (b * abs(a), float.epsilon * 8) and store next line number in ra
bne Branch to line c if a != b
bneal Branch to line c if a != b and store next line number in ra
bnez branch to line b if a != 0
bnezal Branch to line b if a != 0 and store next line number in ra
brap Relative branch to line d if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8)
brapz Relative branch to line c if abs(a) <= max(b * abs(a), float.epsilon * 8)
brdns Relative jump to line a if device is not set
brdse Relative jump to line a if device is set
breq Relative branch to line c if a == b
breqz Relative branch to line b if a == 0
brge Relative jump to line c if a >= b
brgez Relative branch to line b if a >= 0
brgt relative jump to line c if a > b
brgtz Relative branch to line b if a > 0
brle Relative jump to line c if a <= b
brlez Relative branch to line b if a <= 0
brlt Relative jump to line c if a < b
brltz Relative branch to line b if a < 0
brna Relative branch to line d if abs(a - b) > max(c * max(abs(a), abs(b)), float.epsilon * 8)
brnan Relative branch to line b if a is not a number (NaN)
brnaz Relative branch to line c if abs(a) > max(b * abs(a), float.epsilon * 8)
brne Relative branch to line c if a != b
brnez Relative branch to line b if a != 0
ceil Register = smallest integer greater than a
cos Returns the cosine of the specified angle (radians)
define Creates a label that will be replaced throughout the program with the provided value.
div Register = a / b
exp Register = exp(a) or e^a
floor Register = largest integer less than a
hcf Halt and catch fire
j Jump execution to line a
jal Jump execution to line a and store next line number in ra
jr Relative jump to line a
l Loads device LogicType to register by housing index value.
label DEPRECATED
lb Loads LogicType from all output network devices with provided type hash using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
lbn Loads LogicType from all output network devices with provided type and name hashes using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
lbns Loads LogicSlotType from slotIndex from all output network devices with provided type and name hashes using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
lbs Loads LogicSlotType from slotIndex from all output network devices with provided type hash using the provide batch mode. Average (0), Sum (1), Minimum (2), Maximum (3). Can use either the word, or the number.
ld Loads device LogicType to register by direct ID reference.
log Register = base e log(a) or ln(a)
lr Loads reagent of device's ReagentMode where a hash of the reagent type to check for. ReagentMode can be either Contents (0), Required (1), Recipe (2). Can use either the word, or the number.
ls Loads slot LogicSlotType on device to register.
max Register = max of a or b
min Register = min of a or b
mod Register = a mod b (note: NOT a % b)
move Register = provided num or register value.
mul Register = a * b
nor Performs a bitwise logical NOR (NOT OR) operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If both bits are 0, the resulting bit is set to 1. Otherwise, if at least one bit is 1, the resulting bit is set to 0.
not Performs a bitwise logical NOT operation flipping each bit of the input value, resulting in a binary complement. If a bit is 1, it becomes 0, and if a bit is 0, it becomes 1.
or Performs a bitwise logical OR operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If either bit is 1, the resulting bit is set to 1. If both bits are 0, the resulting bit is set to 0.
peek Register = the value at the top of the stack
pop Register = the value at the top of the stack and decrements sp
push Pushes the value of a to the stack at sp and increments sp
rand Register = a random value x with 0 <= x < 1
round Register = a rounded to nearest integer
s Stores register value to LogicType on device by housing index value.
sap Register = 1 if abs(a - b) <= max(c * max(abs(a), abs(b)), float.epsilon * 8), otherwise 0
sapz Register = 1 if abs(a) <= max(b * abs(a), float.epsilon * 8), otherwise 0
sb Stores register value to LogicType on all output network devices with provided type hash.
sbn Stores register value to LogicType on all output network devices with provided type hash and name.
sbs Stores register value to LogicSlotType on all output network devices with provided type hash in the provided slot.
sd Stores register value to LogicType on device by direct ID reference.
sdns Register = 1 if device is not set, otherwise 0
sdse Register = 1 if device is set, otherwise 0.
select Register = b if a is non-zero, otherwise c
seq Register = 1 if a == b, otherwise 0
seqz Register = 1 if a == 0, otherwise 0
sge Register = 1 if a >= b, otherwise 0
sgez Register = 1 if a >= 0, otherwise 0
sgt Register = 1 if a > b, otherwise 0
sgtz Register = 1 if a > 0, otherwise 0
sin Returns the sine of the specified angle (radians)
sla Performs a bitwise arithmetic left shift operation on the binary representation of a value. It shifts the bits to the left and fills the vacated rightmost bits with a copy of the sign bit (the most significant bit).
sle Register = 1 if a <= b, otherwise 0
sleep Pauses execution on the IC for a seconds
slez Register = 1 if a <= 0, otherwise 0
sll Performs a bitwise logical left shift operation on the binary representation of a value. It shifts the bits to the left and fills the vacated rightmost bits with zeros.
slt Register = 1 if a < b, otherwise 0
sltz Register = 1 if a < 0, otherwise 0
sna Register = 1 if abs(a - b) > max(c * max(abs(a), abs(b)), float.epsilon * 8), otherwise 0
snan Register = 1 if a is NaN, otherwise 0
snanz Register = 0 if a is NaN, otherwise 1
snaz Register = 1 if abs(a) > max(b * abs(a), float.epsilon), otherwise 0
sne Register = 1 if a != b, otherwise 0
snez Register = 1 if a != 0, otherwise 0
sqrt Register = square root of a
sra Performs a bitwise arithmetic right shift operation on the binary representation of a value. It shifts the bits to the right and fills the vacated leftmost bits with a copy of the sign bit (the most significant bit).
srl Performs a bitwise logical right shift operation on the binary representation of a value. It shifts the bits to the right and fills the vacated leftmost bits with zeros
ss Stores register value to device stored in a slot LogicSlotType on device.
sub Register = a - b.
tan Returns the tan of the specified angle (radians)
trunc Register = a with fractional part removed
xor Performs a bitwise logical XOR (exclusive OR) operation on the binary representation of two values. Each bit of the result is determined by evaluating the corresponding bits of the input values. If the bits are different (one bit is 0 and the other is 1), the resulting bit is set to 1. If the bits are the same (both 0 or both 1), the resulting bit is set to 0.
yield Pauses execution for 1 tick

View File

@@ -1,269 +0,0 @@
Acceleration 216 Change in velocity. Rockets that are deccelerating when landing will show this as negative value.
Activate 9 1 if device is activated (usually means running), otherwise 0
AirRelease 75 The current state of the air release system, for example AirRelease = 1 for a Hardsuit sets Air Release to On
AlignmentError 243
Apex 238
AutoLand 226 Engages the automatic landing algorithm. The rocket will automatically throttle and turn on and off its engines to achieve a smooth landing.
AutoShutOff 218 Turns off all devices in the rocket upon reaching destination
Bpm 103 Bpm
BurnTimeRemaining 225 Estimated time in seconds until fuel is depleted. Calculated based on current fuel usage.
CelestialHash 242
CelestialParentHash 250
Channel0 165 Channel 0 on a cable network which should be considered volatile
Channel1 166 Channel 1 on a cable network which should be considered volatile
Channel2 167 Channel 2 on a cable network which should be considered volatile
Channel3 168 Channel 3 on a cable network which should be considered volatile
Channel4 169 Channel 4 on a cable network which should be considered volatile
Channel5 170 Channel 5 on a cable network which should be considered volatile
Channel6 171 Channel 6 on a cable network which should be considered volatile
Channel7 172 Channel 7 on a cable network which should be considered volatile
Charge 11 The current charge the device has
Chart 256
ChartedNavPoints 259
ClearMemory 62 When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when actioned
CollectableGoods 101
Color 38 \n Whether driven by concerns for clarity, safety or simple aesthetics, {LINK:Stationeers;Stationeers} have access to a small rainbow of colors for their constructions. These are the color setting for devices, represented as an integer.\n\n0: Blue\n1: Grey\n2: Green\n3: Orange\n4: Red\n5: Yellow\n6: White\n7: Black\n8: Brown\n9: Khaki\n10: Pink\n11: Purple\n\n It is an unwavering universal law that anything higher than 11 will be purple. The {LINK:ODA;ODA} is powerless to change this. Similarly, anything lower than 0 will be Blue.\n
Combustion 98 The assess atmosphere is on fire. Returns 1 if atmosphere is on fire, 0 if not.
CombustionInput 146 The assess atmosphere is on fire. Returns 1 if device's input network is on fire, 0 if not.
CombustionInput2 147 The assess atmosphere is on fire. Returns 1 if device's Input2 network is on fire, 0 if not.
CombustionLimiter 153 Retards the rate of combustion inside the machine (range: 0-100), with 0 being the slowest rate of combustion and 100 being the fastest
CombustionOutput 148 The assess atmosphere is on fire. Returns 1 if device's Output network is on fire, 0 if not.
CombustionOutput2 149 The assess atmosphere is on fire. Returns 1 if device's Output2 network is on fire, 0 if not.
CompletionRatio 61 How complete the current production is for this device, between 0 and 1
ContactTypeId 198 The type id of the contact.
CurrentCode 261
CurrentResearchPodType 93
Density 262
DestinationCode 215 Unique identifier code for a destination on the space map.
Discover 255
DistanceAu 244
DistanceKm 249
DrillCondition 240
DryMass 220 The Mass in kilograms of the rocket excluding fuel. The more massive the rocket the more fuel will be required to move to a new location in space.
Eccentricity 247
ElevatorLevel 40 Level the elevator is currently at
ElevatorSpeed 39 Current speed of the elevator
EntityState 239
EnvironmentEfficiency 104 The Environment Efficiency reported by the machine, as a float between 0 and 1
Error 4 1 if device is in error state, otherwise 0
ExhaustVelocity 235
ExportCount 63 How many items exported since last ClearMemory
ExportQuantity 31 Total quantity of items exported by the device
ExportSlotHash 42 DEPRECATED
ExportSlotOccupant 32 DEPRECATED
Filtration 74 The current state of the filtration system, for example Filtration = 1 for a Hardsuit sets filtration to On
FlightControlRule 236
Flush 174 Set to 1 to activate the flush function on the device
ForceWrite 85 Forces Logic Writer devices to rewrite value
ForwardX 227
ForwardY 228
ForwardZ 229
Fuel 99
Harvest 69 Performs the harvesting action for any plant based machinery
Horizontal 20 Horizontal setting of the device
HorizontalRatio 34 Radio of horizontal setting for device
Idle 37 Returns 1 if the device is currently idle, otherwise 0
ImportCount 64 How many items imported since last ClearMemory
ImportQuantity 29 Total quantity of items imported by the device
ImportSlotHash 43 DEPRECATED
ImportSlotOccupant 30 DEPRECATED
Inclination 246
Index 241
InterrogationProgress 157 Progress of this sattellite dish's interrogation of its current target, as a ratio from 0-1
LineNumber 173 The line number of current execution for an integrated circuit running on this device. While this number can be written, use with caution
Lock 10 1 if device is locked, otherwise 0, can be set in most devices and prevents the user from access the values
ManualResearchRequiredPod 94
Mass 219 The total Mass of the rocket in kilograms including fuel and cargo. The more massive the rocket the more fuel will be required to move to a new location in space.
Maximum 23 Maximum setting of the device
MinWattsToContact 163 Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact
MineablesInQueue 96
MineablesInVicinity 95
MinedQuantity 266
MinimumWattsToContact 163 Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact
Mode 3 Integer for mode state, different devices will have different mode states available to them
NavPoints 258
NextWeatherEventTime 97
None 0 No description
On 28 The current state of the device, 0 for off, 1 for on
Open 2 1 if device is open, otherwise 0
OperationalTemperatureEfficiency 150 How the input pipe's temperature effects the machines efficiency
OrbitPeriod 245
Orientation 230
Output 70 The output operation for a sort handling device, such as a stacker or sorter, when in logic mode the device will only action one repetition when set zero or above and then back to -1 and await further instructions
PassedMoles 234
Plant 68 Performs the planting action for any plant based machinery
PlantEfficiency1 52 DEPRECATED
PlantEfficiency2 53 DEPRECATED
PlantEfficiency3 54 DEPRECATED
PlantEfficiency4 55 DEPRECATED
PlantGrowth1 48 DEPRECATED
PlantGrowth2 49 DEPRECATED
PlantGrowth3 50 DEPRECATED
PlantGrowth4 51 DEPRECATED
PlantHash1 56 DEPRECATED
PlantHash2 57 DEPRECATED
PlantHash3 58 DEPRECATED
PlantHash4 59 DEPRECATED
PlantHealth1 44 DEPRECATED
PlantHealth2 45 DEPRECATED
PlantHealth3 46 DEPRECATED
PlantHealth4 47 DEPRECATED
PositionX 76 The current position in X dimension in world coordinates
PositionY 77 The current position in Y dimension in world coordinates
PositionZ 78 The current position in Z dimension in world coordinates
Power 1 Can be read to return if the device is correctly powered or not, set via the power system, return 1 if powered and 0 if not
PowerActual 26 How much energy the device or network is actually using
PowerGeneration 65 Returns how much power is being generated
PowerPotential 25 How much energy the device or network potentially provides
PowerRequired 36 Power requested from the device and/or network
PrefabHash 84 The hash of the structure
Pressure 5 The current pressure reading of the device
PressureEfficiency 152 How the pressure of the input pipe and waste pipe effect the machines efficiency
PressureExternal 7 Setting for external pressure safety, in KPa
PressureInput 106 The current pressure reading of the device's Input Network
PressureInput2 116 The current pressure reading of the device's Input2 Network
PressureInternal 8 Setting for internal pressure safety, in KPa
PressureOutput 126 The current pressure reading of the device's Output Network
PressureOutput2 136 The current pressure reading of the device's Output2 Network
PressureSetting 71 The current setting for the internal pressure of the object (e.g. the Hardsuit Air release), in KPa
Progress 214 Progress of the rocket to the next node on the map expressed as a value between 0-1.
Quantity 27 Total quantity on the device
Ratio 24 Context specific value depending on device, 0 to 1 based ratio
RatioCarbonDioxide 15 The ratio of {GAS:CarbonDioxide} in device atmosphere
RatioCarbonDioxideInput 109 The ratio of {GAS:CarbonDioxide} in device's input network
RatioCarbonDioxideInput2 119 The ratio of {GAS:CarbonDioxide} in device's Input2 network
RatioCarbonDioxideOutput 129 The ratio of {GAS:CarbonDioxide} in device's Output network
RatioCarbonDioxideOutput2 139 The ratio of {GAS:CarbonDioxide} in device's Output2 network
RatioHydrogen 252 The ratio of {GAS:Hydrogen} in device's Atmopshere
RatioLiquidCarbonDioxide 199 The ratio of {GAS:LiquidCarbonDioxide} in device's Atmosphere
RatioLiquidCarbonDioxideInput 200 The ratio of {GAS:LiquidCarbonDioxide} in device's Input Atmosphere
RatioLiquidCarbonDioxideInput2 201 The ratio of {GAS:LiquidCarbonDioxide} in device's Input2 Atmosphere
RatioLiquidCarbonDioxideOutput 202 The ratio of {GAS:LiquidCarbonDioxide} in device's device's Output Atmosphere
RatioLiquidCarbonDioxideOutput2 203 The ratio of {GAS:LiquidCarbonDioxide} in device's Output2 Atmopshere
RatioLiquidHydrogen 253 The ratio of {GAS:LiquidHydrogen} in device's Atmopshere
RatioLiquidNitrogen 177 The ratio of {GAS:LiquidNitrogen} in device atmosphere
RatioLiquidNitrogenInput 178 The ratio of {GAS:LiquidNitrogen} in device's input network
RatioLiquidNitrogenInput2 179 The ratio of {GAS:LiquidNitrogen} in device's Input2 network
RatioLiquidNitrogenOutput 180 The ratio of {GAS:LiquidNitrogen} in device's Output network
RatioLiquidNitrogenOutput2 181 The ratio of {GAS:LiquidNitrogen} in device's Output2 network
RatioLiquidNitrousOxide 209 The ratio of {GAS:LiquidNitrousOxide} in device's Atmosphere
RatioLiquidNitrousOxideInput 210 The ratio of {GAS:LiquidNitrousOxide} in device's Input Atmosphere
RatioLiquidNitrousOxideInput2 211 The ratio of {GAS:LiquidNitrousOxide} in device's Input2 Atmosphere
RatioLiquidNitrousOxideOutput 212 The ratio of {GAS:LiquidNitrousOxide} in device's device's Output Atmosphere
RatioLiquidNitrousOxideOutput2 213 The ratio of {GAS:LiquidNitrousOxide} in device's Output2 Atmopshere
RatioLiquidOxygen 183 The ratio of {GAS:LiquidOxygen} in device's Atmosphere
RatioLiquidOxygenInput 184 The ratio of {GAS:LiquidOxygen} in device's Input Atmosphere
RatioLiquidOxygenInput2 185 The ratio of {GAS:LiquidOxygen} in device's Input2 Atmosphere
RatioLiquidOxygenOutput 186 The ratio of {GAS:LiquidOxygen} in device's device's Output Atmosphere
RatioLiquidOxygenOutput2 187 The ratio of {GAS:LiquidOxygen} in device's Output2 Atmopshere
RatioLiquidPollutant 204 The ratio of {GAS:LiquidPollutant} in device's Atmosphere
RatioLiquidPollutantInput 205 The ratio of {GAS:LiquidPollutant} in device's Input Atmosphere
RatioLiquidPollutantInput2 206 The ratio of {GAS:LiquidPollutant} in device's Input2 Atmosphere
RatioLiquidPollutantOutput 207 The ratio of {GAS:LiquidPollutant} in device's device's Output Atmosphere
RatioLiquidPollutantOutput2 208 The ratio of {GAS:LiquidPollutant} in device's Output2 Atmopshere
RatioLiquidVolatiles 188 The ratio of {GAS:LiquidVolatiles} in device's Atmosphere
RatioLiquidVolatilesInput 189 The ratio of {GAS:LiquidVolatiles} in device's Input Atmosphere
RatioLiquidVolatilesInput2 190 The ratio of {GAS:LiquidVolatiles} in device's Input2 Atmosphere
RatioLiquidVolatilesOutput 191 The ratio of {GAS:LiquidVolatiles} in device's device's Output Atmosphere
RatioLiquidVolatilesOutput2 192 The ratio of {GAS:LiquidVolatiles} in device's Output2 Atmopshere
RatioNitrogen 16 The ratio of nitrogen in device atmosphere
RatioNitrogenInput 110 The ratio of nitrogen in device's input network
RatioNitrogenInput2 120 The ratio of nitrogen in device's Input2 network
RatioNitrogenOutput 130 The ratio of nitrogen in device's Output network
RatioNitrogenOutput2 140 The ratio of nitrogen in device's Output2 network
RatioNitrousOxide 83 The ratio of {GAS:NitrousOxide} in device atmosphere
RatioNitrousOxideInput 114 The ratio of {GAS:NitrousOxide} in device's input network
RatioNitrousOxideInput2 124 The ratio of {GAS:NitrousOxide} in device's Input2 network
RatioNitrousOxideOutput 134 The ratio of {GAS:NitrousOxide} in device's Output network
RatioNitrousOxideOutput2 144 The ratio of {GAS:NitrousOxide} in device's Output2 network
RatioOxygen 14 The ratio of oxygen in device atmosphere
RatioOxygenInput 108 The ratio of oxygen in device's input network
RatioOxygenInput2 118 The ratio of oxygen in device's Input2 network
RatioOxygenOutput 128 The ratio of oxygen in device's Output network
RatioOxygenOutput2 138 The ratio of oxygen in device's Output2 network
RatioPollutant 17 The ratio of pollutant in device atmosphere
RatioPollutantInput 111 The ratio of pollutant in device's input network
RatioPollutantInput2 121 The ratio of pollutant in device's Input2 network
RatioPollutantOutput 131 The ratio of pollutant in device's Output network
RatioPollutantOutput2 141 The ratio of pollutant in device's Output2 network
RatioPollutedWater 254 The ratio of polluted water in device atmosphere
RatioSteam 193 The ratio of {GAS:Steam} in device's Atmosphere
RatioSteamInput 194 The ratio of {GAS:Steam} in device's Input Atmosphere
RatioSteamInput2 195 The ratio of {GAS:Steam} in device's Input2 Atmosphere
RatioSteamOutput 196 The ratio of {GAS:Steam} in device's device's Output Atmosphere
RatioSteamOutput2 197 The ratio of {GAS:Steam} in device's Output2 Atmopshere
RatioVolatiles 18 The ratio of volatiles in device atmosphere
RatioVolatilesInput 112 The ratio of volatiles in device's input network
RatioVolatilesInput2 122 The ratio of volatiles in device's Input2 network
RatioVolatilesOutput 132 The ratio of volatiles in device's Output network
RatioVolatilesOutput2 142 The ratio of volatiles in device's Output2 network
RatioWater 19 The ratio of water in device atmosphere
RatioWaterInput 113 The ratio of water in device's input network
RatioWaterInput2 123 The ratio of water in device's Input2 network
RatioWaterOutput 133 The ratio of water in device's Output network
RatioWaterOutput2 143 The ratio of water in device's Output2 network
ReEntryAltitude 237
Reagents 13 Total number of reagents recorded by the device
RecipeHash 41 Current hash of the recipe the device is set to produce
ReferenceId 217
RequestHash 60 When set to the unique identifier, requests an item of the provided type from the device
RequiredPower 33 Idle operating power quantity, does not necessarily include extra demand power
ReturnFuelCost 100
Richness 263
Rpm 155 The number of revolutions per minute that the device's spinning mechanism is doing
SemiMajorAxis 248
Setting 12 A variable setting that can be read or written, depending on the device
SettingInput 91
SettingInputHash 91 The input setting for the device
SettingOutput 92
SettingOutputHash 91 The output setting for the device
SignalID 87 Returns the contact ID of the strongest signal from this Satellite
SignalStrength 86 Returns the degree offset of the strongest contact
Sites 260
Size 264
SizeX 160 Size on the X (right) axis of the object in largeGrids (a largeGrid is 2meters)
SizeY 161 Size on the Y(Up) axis of the object in largeGrids (a largeGrid is 2meters)
SizeZ 162 Size on the Z(Forward) axis of the object in largeGrids (a largeGrid is 2meters)
SolarAngle 22 Solar angle of the device
SolarIrradiance 176
SoundAlert 175 Plays a sound alert on the devices speaker
Stress 156 Machines get stressed when working hard. When Stress reaches 100 the machine will automatically shut down
Survey 257
TargetPadIndex 158 The index of the trader landing pad on this devices data network that it will try to call a trader in to land
TargetX 88 The target position in X dimension in world coordinates
TargetY 89 The target position in Y dimension in world coordinates
TargetZ 90 The target position in Z dimension in world coordinates
Temperature 6 The current temperature reading of the device
TemperatureDifferentialEfficiency 151 How the difference between the input pipe and waste pipe temperatures effect the machines efficiency
TemperatureExternal 73 The temperature of the outside of the device, usually the world atmosphere surrounding it
TemperatureInput 107 The current temperature reading of the device's Input Network
TemperatureInput2 117 The current temperature reading of the device's Input2 Network
TemperatureOutput 127 The current temperature reading of the device's Output Network
TemperatureOutput2 137 The current temperature reading of the device's Output2 Network
TemperatureSetting 72 The current setting for the internal temperature of the object (e.g. the Hardsuit A/C)
Throttle 154 Increases the rate at which the machine works (range: 0-100)
Thrust 221 Total current thrust of all rocket engines on the rocket in Newtons.
ThrustToWeight 223 Ratio of thrust to weight of rocket. Weight is effected by local body gravity. A rocket with a low thrust to weight will expend more fuel during launch and landing.
Time 102 Time
TimeToDestination 224 Estimated time in seconds until rocket arrives at target destination.
TotalMoles 66 Returns the total moles of the device
TotalMolesInput 115 Returns the total moles of the device's Input Network
TotalMolesInput2 125 Returns the total moles of the device's Input2 Network
TotalMolesOutput 135 Returns the total moles of the device's Output Network
TotalMolesOutput2 145 Returns the total moles of the device's Output2 Network
TotalQuantity 265
TrueAnomaly 251
VelocityMagnitude 79 The current magnitude of the velocity vector
VelocityRelativeX 80 The current velocity X relative to the forward vector of this
VelocityRelativeY 81 The current velocity Y relative to the forward vector of this
VelocityRelativeZ 82 The current velocity Z relative to the forward vector of this
VelocityX 231
VelocityY 232
VelocityZ 233
Vertical 21 Vertical setting of the device
VerticalRatio 35 Radio of vertical setting for device
Volume 67 Returns the device atmosphere volume
VolumeOfLiquid 182 The total volume of all liquids in Liters in the atmosphere
WattsReachingContact 164 The amount of watts actually hitting the contact. This is effected by the power of the dish and how far off-axis the dish is from the contact vector
Weight 222 Weight of Rocket in Newtons (Including fuel and cargo). Weight is effected by local body gravity.
WorkingGasEfficiency 105 The Working Gas Efficiency reported by the machine, as a float between 0 and 1

View File

@@ -1,4 +0,0 @@
Contents 0 The amount of this Reagent present in the machine
Recipe 2 The amount of this Reagent required by the Machine's current recipe
Required 1 The amount of this Reagent needed to complete the Machine's current recipe after subtracting the amount currently present
TotalContents 3

View File

@@ -1,27 +0,0 @@
Charge 10 returns current energy charge the slot occupant is holding
ChargeRatio 11 returns current energy charge the slot occupant is holding as a ratio between 0 and 1 of its maximum
Class 12 returns integer representing the class of object
Damage 4 returns the damage state of the item in the slot
Efficiency 5 returns the growth efficiency of the plant in the slot
FilterType 25
Growth 7 returns the current growth state of the plant in the slot
Health 6 returns the health of the plant in the slot
LineNumber 19
Lock 23
Mature 16 returns 1 if the plant in this slot is mature, 0 when it isn't
MaxQuantity 15 returns the max stack size of the item in the slot
None 0 No description
OccupantHash 2 returns the has of the current occupant, the unique identifier of the thing
Occupied 1 returns 0 when slot is not occupied, 1 when it is
On 22
Open 21
PrefabHash 17 returns the hash of the structure in the slot
Pressure 8 returns pressure of the slot occupants internal atmosphere
PressureAir 14 returns pressure in the air tank of the jetpack in this slot
PressureWaste 13 returns pressure in the waste tank of the jetpack in this slot
Quantity 3 returns the current quantity, such as stack size, of the item in the slot
ReferenceId 26
Seeding 18 Whether a plant is seeding (ready to harvest seeds from). Returns 1 if seeding or 0 if not.
SortingClass 24
Temperature 9 returns temperature of the slot occupants internal atmosphere
Volume 20

File diff suppressed because it is too large Load Diff

View File

@@ -210,7 +210,7 @@ def extract_data(install_path: Path, data_path: Path, language: str):
continue
key = key.text
value = value.text
if key is None:
if key is None or any(bad in key for bad in "(){}|[]") :
continue
crc_u = binascii.crc32(key.encode('utf-8'))
crc_i: int = struct.unpack("i", struct.pack("I", crc_u))[0]

View File

@@ -1,994 +0,0 @@
use crate::{
grammar::{LogicType, ReagentMode, SlotLogicType},
interpreter::{ICError, ICState},
network::{CableConnectionType, Connection},
vm::VM,
};
use std::{collections::BTreeMap, ops::Deref};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, EnumIter, EnumString};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FieldType {
Read,
Write,
ReadWrite,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogicField {
pub field_type: FieldType,
pub value: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SlotOccupant {
pub id: u32,
pub prefab_hash: i32,
pub quantity: u32,
pub max_quantity: u32,
pub sorting_class: SortingClass,
pub damage: f64,
fields: BTreeMap<SlotLogicType, LogicField>,
}
impl SlotOccupant {
pub fn from_template<F>(template: SlotOccupantTemplate, id_fn: F) -> Self
where
F: FnOnce() -> u32,
{
let mut fields = template.fields;
SlotOccupant {
id: template.id.unwrap_or_else(id_fn),
prefab_hash: fields
.remove(&SlotLogicType::PrefabHash)
.map(|field| field.value as i32)
.unwrap_or(0),
quantity: fields
.remove(&SlotLogicType::Quantity)
.map(|field| field.value as u32)
.unwrap_or(1),
max_quantity: fields
.remove(&SlotLogicType::MaxQuantity)
.map(|field| field.value as u32)
.unwrap_or(1),
damage: fields
.remove(&SlotLogicType::Damage)
.map(|field| field.value)
.unwrap_or(0.0),
sorting_class: fields
.remove(&SlotLogicType::SortingClass)
.map(|field| (field.value as u32).into())
.unwrap_or(SortingClass::Default),
fields,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SlotOccupantTemplate {
pub id: Option<u32>,
pub fields: BTreeMap<SlotLogicType, LogicField>,
}
impl SlotOccupant {
pub fn new(id: u32, prefab_hash: i32) -> Self {
SlotOccupant {
id,
prefab_hash,
quantity: 1,
max_quantity: 1,
damage: 0.0,
sorting_class: SortingClass::Default,
fields: BTreeMap::new(),
}
}
/// chainable constructor
pub fn with_quantity(mut self, quantity: u32) -> Self {
self.quantity = quantity;
self
}
/// chainable constructor
pub fn with_max_quantity(mut self, max_quantity: u32) -> Self {
self.max_quantity = max_quantity;
self
}
/// chainable constructor
pub fn with_damage(mut self, damage: f64) -> Self {
self.damage = damage;
self
}
/// chainable constructor
pub fn with_fields(mut self, fields: BTreeMap<SlotLogicType, LogicField>) -> Self {
self.fields.extend(fields);
self
}
/// chainable constructor
pub fn get_fields(&self) -> BTreeMap<SlotLogicType, LogicField> {
let mut copy = self.fields.clone();
copy.insert(
SlotLogicType::PrefabHash,
LogicField {
field_type: FieldType::Read,
value: self.prefab_hash as f64,
},
);
copy.insert(
SlotLogicType::SortingClass,
LogicField {
field_type: FieldType::Read,
value: self.sorting_class as u32 as f64,
},
);
copy.insert(
SlotLogicType::Quantity,
LogicField {
field_type: FieldType::Read,
value: self.quantity as f64,
},
);
copy.insert(
SlotLogicType::MaxQuantity,
LogicField {
field_type: FieldType::Read,
value: self.max_quantity as f64,
},
);
copy.insert(
SlotLogicType::Damage,
LogicField {
field_type: FieldType::Read,
value: self.damage,
},
);
copy
}
pub fn set_field(&mut self, typ: SlotLogicType, val: f64, force: bool) -> Result<(), ICError> {
if (typ == SlotLogicType::Quantity) && force {
self.quantity = val as u32;
Ok(())
} else if (typ == SlotLogicType::MaxQuantity) && force {
self.max_quantity = val as u32;
Ok(())
} else if (typ == SlotLogicType::Damage) && force {
self.damage = val;
Ok(())
} else if let Some(logic) = self.fields.get_mut(&typ) {
match logic.field_type {
FieldType::ReadWrite | FieldType::Write => {
logic.value = val;
Ok(())
}
_ => {
if force {
logic.value = val;
Ok(())
} else {
Err(ICError::ReadOnlyField(typ.to_string()))
}
}
}
} else if force {
self.fields.insert(
typ,
LogicField {
field_type: FieldType::ReadWrite,
value: val,
},
);
Ok(())
} else {
Err(ICError::ReadOnlyField(typ.to_string()))
}
}
pub fn can_logic_read(&self, field: SlotLogicType) -> bool {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Read | FieldType::ReadWrite)
} else {
false
}
}
pub fn can_logic_write(&self, field: SlotLogicType) -> bool {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Write | FieldType::ReadWrite)
} else {
false
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Slot {
pub typ: SlotType,
pub occupant: Option<SlotOccupant>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SlotTemplate {
pub typ: SlotType,
pub occupant: Option<SlotOccupantTemplate>,
}
impl Slot {
pub fn new(typ: SlotType) -> Self {
Slot {
typ,
occupant: None,
}
}
pub fn with_occupant(typ: SlotType, occupant: SlotOccupant) -> Self {
Slot {
typ,
occupant: Some(occupant),
}
}
pub fn get_fields(&self) -> BTreeMap<SlotLogicType, LogicField> {
let mut copy = self
.occupant
.as_ref()
.map(|occupant| occupant.get_fields())
.unwrap_or_default();
copy.insert(
SlotLogicType::Occupied,
LogicField {
field_type: FieldType::Read,
value: if self.occupant.is_some() { 1.0 } else { 0.0 },
},
);
copy.insert(
SlotLogicType::OccupantHash,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.prefab_hash as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Quantity,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.quantity as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Damage,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.damage)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Class,
LogicField {
field_type: FieldType::Read,
value: self.typ as u32 as f64,
},
);
copy.insert(
SlotLogicType::MaxQuantity,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.max_quantity as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::PrefabHash,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.prefab_hash as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::SortingClass,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.sorting_class as u32 as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::ReferenceId,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.id as f64)
.unwrap_or(0.0),
},
);
copy
}
pub fn get_field(&self, field: SlotLogicType) -> f64 {
let fields = self.get_fields();
fields
.get(&field)
.map(|field| match field.field_type {
FieldType::Read | FieldType::ReadWrite => field.value,
_ => 0.0,
})
.unwrap_or(0.0)
}
pub fn can_logic_read(&self, field: SlotLogicType) -> bool {
match field {
SlotLogicType::Pressure | SlotLogicType::Temperature | SlotLogicType::Volume => {
matches!(
self.typ,
SlotType::GasCanister | SlotType::LiquidCanister | SlotType::LiquidBottle
)
}
SlotLogicType::Charge | SlotLogicType::ChargeRatio => {
matches!(self.typ, SlotType::Battery)
}
SlotLogicType::Open => matches!(
self.typ,
SlotType::Helmet | SlotType::Tool | SlotType::Appliance
),
SlotLogicType::Lock => matches!(self.typ, SlotType::Helmet),
SlotLogicType::FilterType => matches!(self.typ, SlotType::GasFilter),
_ => {
if let Some(occupant) = self.occupant.as_ref() {
occupant.can_logic_read(field)
} else {
false
}
}
}
}
pub fn can_logic_write(&self, field: SlotLogicType) -> bool {
match field {
SlotLogicType::Open => matches!(
self.typ,
SlotType::Helmet
| SlotType::GasCanister
| SlotType::LiquidCanister
| SlotType::LiquidBottle
),
SlotLogicType::On => matches!(
self.typ,
SlotType::Helmet | SlotType::Tool | SlotType::Appliance
),
SlotLogicType::Lock => matches!(self.typ, SlotType::Helmet),
_ => {
if let Some(occupant) = self.occupant.as_ref() {
occupant.can_logic_write(field)
} else {
false
}
}
}
}
pub fn set_field(&mut self, typ: SlotLogicType, val: f64, force: bool) -> Result<(), ICError> {
if matches!(
typ,
SlotLogicType::Occupied
| SlotLogicType::OccupantHash
| SlotLogicType::Class
| SlotLogicType::PrefabHash
| SlotLogicType::SortingClass
| SlotLogicType::ReferenceId
) {
return Err(ICError::ReadOnlyField(typ.to_string()));
}
if let Some(occupant) = self.occupant.as_mut() {
occupant.set_field(typ, val, force)
} else {
Err(ICError::SlotNotOccupied)
}
}
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
Hash,
strum_macros::Display,
EnumString,
EnumIter,
AsRefStr,
Serialize,
Deserialize,
)]
#[strum(serialize_all = "PascalCase")]
pub enum SortingClass {
#[default]
Default = 0,
Kits = 1,
Tools = 2,
Resources,
Food = 4,
Clothing,
Appliances,
Atmospherics,
Storage = 8,
Ores,
Ices,
}
impl From<u32> for SortingClass {
fn from(value: u32) -> Self {
match value {
1 => Self::Kits,
2 => Self::Tools,
3 => Self::Resources,
4 => Self::Food,
5 => Self::Clothing,
6 => Self::Appliances,
7 => Self::Atmospherics,
8 => Self::Storage,
9 => Self::Ores,
10 => Self::Ices,
_ => Self::Default,
}
}
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
Hash,
strum_macros::Display,
EnumString,
EnumIter,
AsRefStr,
Serialize,
Deserialize,
)]
#[strum(serialize_all = "PascalCase")]
pub enum SlotType {
Helmet = 1,
Suit = 2,
Back,
GasFilter = 4,
GasCanister,
MotherBoard,
Circuitboard,
DataDisk = 8,
Organ,
Ore,
Plant,
Uniform,
Entity,
Battery,
Egg,
Belt = 16,
Tool,
Appliance,
Ingot,
Torpedo,
Cartridge,
AccessCard,
Magazine,
Circuit = 24,
Bottle,
ProgrammableChip,
Glasses,
CreditCard,
DirtCanister,
SensorProcessingUnit,
LiquidCanister,
LiquidBottle = 32,
Wreckage,
SoundCartridge,
DrillHead,
ScanningHead,
Flare,
Blocked,
#[default]
None = 0,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Prefab {
pub name: String,
pub hash: i32,
}
impl Prefab {
pub fn new(name: &str) -> Self {
Prefab {
name: name.to_owned(),
hash: const_crc32::crc32(name.as_bytes()) as i32,
}
}
}
#[derive(Debug, Default)]
pub struct Device {
pub id: u32,
pub name: Option<String>,
pub name_hash: Option<i32>,
pub prefab: Option<Prefab>,
pub slots: Vec<Slot>,
pub reagents: BTreeMap<ReagentMode, BTreeMap<i32, f64>>,
pub ic: Option<u32>,
pub connections: Vec<Connection>,
fields: BTreeMap<LogicType, LogicField>,
}
impl Device {
pub fn new(id: u32) -> Self {
Device {
id,
name: None,
name_hash: None,
prefab: None,
fields: BTreeMap::new(),
slots: Vec::new(),
reagents: BTreeMap::new(),
ic: None,
connections: vec![Connection::CableNetwork {
net: None,
typ: CableConnectionType::default(),
}],
}
}
pub fn with_ic(id: u32, ic: u32) -> Self {
let mut device = Device::new(id);
device.ic = Some(ic);
device.connections = vec![
Connection::CableNetwork {
net: None,
typ: CableConnectionType::Data,
},
Connection::CableNetwork {
net: None,
typ: CableConnectionType::Power,
},
];
device.prefab = Some(Prefab::new("StructureCircuitHousing"));
device.fields.extend(vec![
(
LogicType::Setting,
LogicField {
field_type: FieldType::ReadWrite,
value: 0.0,
},
),
(
LogicType::RequiredPower,
LogicField {
field_type: FieldType::Read,
value: 0.0,
},
),
(
LogicType::PrefabHash,
LogicField {
field_type: FieldType::Read,
value: -128473777.0,
},
),
]);
let occupant = SlotOccupant::new(ic, -744098481);
device.slots.push(Slot::with_occupant(
SlotType::ProgrammableChip,
// -744098481 = ItemIntegratedCircuit10
occupant,
));
device
}
pub fn get_fields(&self, vm: &VM) -> BTreeMap<LogicType, LogicField> {
let mut copy = self.fields.clone();
if let Some(ic_id) = &self.ic {
let ic = vm.ics.get(ic_id).expect("our own ic to exist").borrow();
copy.insert(
LogicType::LineNumber,
LogicField {
field_type: FieldType::ReadWrite,
value: ic.ip() as f64,
},
);
copy.insert(
LogicType::Error,
LogicField {
field_type: FieldType::Read,
value: match *ic.state.borrow() {
ICState::Error(_) => 1.0,
_ => 0.0,
},
},
);
}
if self.has_power_state() {
copy.insert(
LogicType::Power,
LogicField {
field_type: FieldType::Read,
value: if self.has_power_connection() {
1.0
} else {
0.0
},
},
);
}
copy.insert(
LogicType::ReferenceId,
LogicField {
field_type: FieldType::Read,
value: self.id as f64,
},
);
copy
}
pub fn get_network_id(&self, connection: usize) -> Result<u32, ICError> {
if connection >= self.connections.len() {
Err(ICError::ConnectionIndexOutOfRange(
connection,
self.connections.len(),
))
} else if let Connection::CableNetwork {
net: network_id, ..
} = self.connections[connection]
{
if let Some(network_id) = network_id {
Ok(network_id)
} else {
Err(ICError::NetworkNotConnected(connection))
}
} else {
Err(ICError::NotACableConnection(connection))
}
}
pub fn can_logic_read(&self, field: LogicType) -> bool {
match field {
LogicType::ReferenceId => true,
LogicType::LineNumber | LogicType::Error if self.ic.is_some() => true,
LogicType::Power if self.has_power_state() => true,
_ => {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Read | FieldType::ReadWrite)
} else {
false
}
}
}
}
pub fn can_logic_write(&self, field: LogicType) -> bool {
match field {
LogicType::ReferenceId => false,
LogicType::LineNumber if self.ic.is_some() => true,
_ => {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Write | FieldType::ReadWrite)
} else {
false
}
}
}
}
pub fn can_slot_logic_read(&self, field: SlotLogicType, slot: usize) -> bool {
if self.slots.is_empty() {
return false;
}
let Some(slot) = self.slots.get(slot) else {
return false;
};
slot.can_logic_read(field)
}
pub fn can_slot_logic_write(&self, field: SlotLogicType, slot: usize) -> bool {
if self.slots.is_empty() {
return false;
}
let Some(slot) = self.slots.get(slot) else {
return false;
};
slot.can_logic_write(field)
}
pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result<f64, ICError> {
if typ == LogicType::LineNumber && self.ic.is_some() {
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.borrow();
Ok(ic.ip() as f64)
} else if let Some(field) = self.get_fields(vm).get(&typ) {
if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite {
Ok(field.value)
} else {
Err(ICError::WriteOnlyField(typ.to_string()))
}
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
}
pub fn set_field(
&mut self,
typ: LogicType,
val: f64,
vm: &VM,
force: bool,
) -> Result<(), ICError> {
if typ == LogicType::ReferenceId
|| (typ == LogicType::Error && self.ic.is_some())
|| (typ == LogicType::Power && self.has_power_state())
{
Err(ICError::ReadOnlyField(typ.to_string()))
} else if typ == LogicType::LineNumber && self.ic.is_some() {
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.borrow();
ic.set_ip(val as u32);
Ok(())
} else if let Some(field) = self.fields.get_mut(&typ) {
if field.field_type == FieldType::Write
|| field.field_type == FieldType::ReadWrite
|| force
{
field.value = val;
Ok(())
} else {
Err(ICError::ReadOnlyField(typ.to_string()))
}
} else if force {
self.fields.insert(
typ,
LogicField {
field_type: FieldType::ReadWrite,
value: val,
},
);
Ok(())
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
}
pub fn get_slot_field(&self, index: f64, typ: SlotLogicType, vm: &VM) -> Result<f64, ICError> {
let slot = self
.slots
.get(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))?;
if slot.typ == SlotType::ProgrammableChip
&& slot.occupant.is_some()
&& self.ic.is_some()
&& typ == SlotLogicType::LineNumber
{
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.borrow();
Ok(ic.ip() as f64)
} else {
Ok(slot.get_field(typ))
}
}
pub fn get_slot_fields(
&self,
index: f64,
vm: &VM,
) -> Result<BTreeMap<SlotLogicType, LogicField>, ICError> {
let slot = self
.slots
.get(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))?;
let mut fields = slot.get_fields();
if slot.typ == SlotType::ProgrammableChip && slot.occupant.is_some() && self.ic.is_some() {
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.borrow();
fields.insert(
SlotLogicType::LineNumber,
LogicField {
field_type: FieldType::ReadWrite,
value: ic.ip() as f64,
},
);
}
Ok(fields)
}
pub fn set_slot_field(
&mut self,
index: f64,
typ: SlotLogicType,
val: f64,
_vm: &VM,
force: bool,
) -> Result<(), ICError> {
let slot = self
.slots
.get_mut(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))?;
slot.set_field(typ, val, force)
}
pub fn get_slot(&self, index: f64) -> Result<&Slot, ICError> {
self.slots
.get(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))
}
pub fn get_reagent(&self, rm: &ReagentMode, reagent: f64) -> f64 {
if let Some(mode) = self.reagents.get(rm) {
if let Some(val) = mode.get(&(reagent as i32)) {
return *val;
}
}
0.0
}
pub fn set_name(&mut self, name: &str) {
self.name_hash = Some(const_crc32::crc32(name.as_bytes()) as i32);
self.name = Some(name.to_owned());
}
pub fn has_power_state(&self) -> bool {
self.connections.iter().any(|conn| {
matches!(
conn,
Connection::CableNetwork {
typ: CableConnectionType::Power | CableConnectionType::PowerAndData,
..
}
)
})
}
pub fn has_power_connection(&self) -> bool {
self.connections.iter().any(|conn| {
matches!(
conn,
Connection::CableNetwork {
net: Some(_),
typ: CableConnectionType::Power | CableConnectionType::PowerAndData,
}
)
})
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct DeviceTemplate {
pub id: Option<u32>,
pub name: Option<String>,
pub prefab_name: Option<String>,
pub slots: Vec<SlotTemplate>,
// pub reagents: BTreeMap<ReagentMode, BTreeMap<i32, f64>>,
pub connections: Vec<Connection>,
pub fields: BTreeMap<LogicType, LogicField>,
}
impl Device {
/// create a devive from a template and return the device, does not create it's own IC
pub fn from_template<F>(template: DeviceTemplate, mut id_fn: F) -> Self
where
F: FnMut() -> u32,
{
// id_fn *must* be captured not moved
#[allow(clippy::redundant_closure)]
let device_id = template.id.unwrap_or_else(|| id_fn());
let name_hash = template
.name
.as_ref()
.map(|name| const_crc32::crc32(name.as_bytes()) as i32);
#[allow(clippy::redundant_closure)]
let slots = template
.slots
.into_iter()
.map(|slot| Slot {
typ: slot.typ,
occupant: slot
.occupant
.map(|occupant| SlotOccupant::from_template(occupant, || id_fn())),
})
.collect_vec();
let ic = slots
.iter()
.find_map(|slot| {
if slot.typ == SlotType::ProgrammableChip && slot.occupant.is_some() {
Some(slot.occupant.clone()).flatten()
} else {
None
}
})
.map(|occupant| occupant.id);
let fields = template.fields;
Device {
id: device_id,
name: template.name,
name_hash,
prefab: template.prefab_name.map(|name| Prefab::new(&name)),
slots,
// reagents: template.reagents,
reagents: BTreeMap::new(),
ic,
connections: template.connections,
fields,
}
}
}
impl<T> From<T> for DeviceTemplate
where
T: Deref<Target = Device>,
{
fn from(device: T) -> Self {
DeviceTemplate {
id: Some(device.id),
name: device.name.clone(),
prefab_name: device.prefab.as_ref().map(|prefab| prefab.name.clone()),
slots: device
.slots
.iter()
.map(|slot| SlotTemplate {
typ: slot.typ,
occupant: slot.occupant.as_ref().map(|occupant| SlotOccupantTemplate {
id: Some(occupant.id),
fields: occupant.get_fields(),
}),
})
.collect_vec(),
connections: device.connections.clone(),
fields: device.fields.clone(),
}
}
}

297
ic10emu/src/errors.rs Normal file
View File

@@ -0,0 +1,297 @@
use crate::vm::{
instructions::enums::InstructionOp,
object::{
errors::{LogicError, MemoryError},
templates::Prefab,
ObjectID,
},
};
use serde_derive::{Deserialize, Serialize};
use stationeers_data::templates::ObjectTemplate;
use std::error::Error as StdError;
use std::fmt::Display;
use thiserror::Error;
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
#[derive(Error, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum VMError {
#[error("device with id '{0}' does not exist")]
UnknownId(ObjectID),
#[error("ic with id '{0}' does not exist")]
UnknownIcId(ObjectID),
#[error("ic encountered an error: {0}")]
ICError(#[from] ICError),
#[error("ic encountered an error: {0}")]
LineError(#[from] LineError),
#[error("invalid network id {0}")]
InvalidNetwork(ObjectID),
#[error("device {0} not visible to device {1} (not on the same networks)")]
DeviceNotVisible(u32, u32),
#[error("a device with id {0} already exists")]
IdInUse(u32),
#[error("device(s) with ids {0:?} already exist")]
IdsInUse(Vec<u32>),
#[error("attempt to use a set of id's with duplicates: id(s) {0:?} exist more than once")]
DuplicateIds(Vec<u32>),
#[error("object {0} is not a device")]
NotADevice(ObjectID),
#[error("device object {0} has no pins")]
NoDevicePins(ObjectID),
#[error("object {0} has no slots")]
NotStorage(ObjectID),
#[error("object {0} is not an item")]
NotAnItem(ObjectID),
#[error("object {0} is not programmable")]
NotProgrammable(ObjectID),
#[error("object {0} is not memory writable")]
NotMemoryWritable(ObjectID),
#[error("object {0} is not a circuit holder or programmable")]
NotCircuitHolderOrProgrammable(ObjectID),
#[error("object {0} is not a circuit holder or memory writable")]
NotCircuitHolderOrMemoryWritable(ObjectID),
#[error("object {0} is a circuit holder but there is no programmable ic present")]
NoIC(ObjectID),
#[error("{0}")]
TemplateError(#[from] TemplateError),
#[error("missing child object {0}")]
MissingChild(ObjectID),
#[error("object {0} is not parentable")]
NotParentable(ObjectID),
#[error("object {0} is not logicable")]
NotLogicable(ObjectID),
#[error("network object {0} is not a network")]
NonNetworkNetwork(ObjectID),
}
#[derive(Error, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum TemplateError {
#[error("object id {0} has a non conforming set of interfaces")]
NonConformingObject(ObjectID),
#[error("object id {0} is missing from the VM")]
MissingVMObject(ObjectID),
#[error("database has no template for prefab {0}")]
NoTemplateForPrefab(Prefab),
#[error("no prefab provided")]
MissingPrefab,
#[error("incorrect template for concrete impl {0} from prefab {1}: {2:?}")]
IncorrectTemplate(String, Prefab, ObjectTemplate),
#[error("frozen memory size error: {0} is not {1}")]
MemorySize(usize, usize),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct LineError {
pub error: ICError,
pub line: u32,
pub msg: String,
}
impl Display for LineError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error on line {}: {}", self.line, self.error)
}
}
impl StdError for LineError {}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ParseError {
pub line: usize,
pub start: usize,
pub end: usize,
pub msg: String,
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} at line {} {}:{}",
self.msg, self.line, self.start, self.end
)
}
}
impl StdError for ParseError {}
impl ParseError {
/// Offset the ParseError in it's line, adding the passed values to it's `start` and `end`
#[must_use]
pub fn offset(self, offset: usize) -> Self {
ParseError {
start: self.start + offset,
end: self.end + offset,
..self
}
}
/// Offset the ParseError line, adding the passed value to it's `line`
#[must_use]
pub fn offset_line(self, offset: usize) -> Self {
ParseError {
line: self.line + offset,
start: self.start,
..self
}
}
/// Mark the parse error as extending 'length' bytes from `start`
#[must_use]
pub fn span(self, length: usize) -> Self {
ParseError {
start: self.start,
end: self.start + length,
..self
}
}
}
#[derive(Debug, Error, Clone, Serialize, Deserialize)]
#[serde(tag = "typ")]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum ICError {
#[error("error compiling code: {0}")]
ParseError(#[from] ParseError),
#[error("{0}")]
LogicError(#[from] LogicError),
#[error("{0}")]
MemoryError(#[from] MemoryError),
#[error("duplicate label {label}: first encountered on line {source_line}")]
DuplicateLabel {
label: String,
line: u32,
source_line: u32,
},
#[error("instruction pointer out of range: '{0}'")]
InstructionPointerOutOfRange(usize),
#[error("register pointer out of range: '{0}'")]
RegisterIndexOutOfRange(f64),
#[error("device pointer out of range: '{0}'")]
DeviceIndexOutOfRange(f64),
#[error("stack index out of range: '{0}'")]
StackIndexOutOfRange(f64),
#[error("slot index out of range: '{0}'")]
SlotIndexOutOfRange(f64),
#[error("pin index {0} out of range 0-6")]
PinIndexOutOfRange(usize),
#[error("connection index {index} out of range {range}")]
ConnectionIndexOutOfRange { index: usize, range: usize },
#[error("unknown device ID '{0}'")]
UnknownDeviceID(f64),
#[error("too few operands!: provide: '{provided}', desired: '{desired}'")]
TooFewOperands { provided: u32, desired: u32 },
#[error("too many operands!: provide: '{provided}', desired: '{desired}'")]
TooManyOperands { provided: u32, desired: u32 },
#[error("incorrect operand type for instruction `{inst}` operand {index}, not a {desired} ")]
IncorrectOperandType {
inst: InstructionOp,
index: usize,
desired: String,
},
#[error("unknown identifier {0}")]
UnknownIdentifier(String),
#[error("device Not Set")]
DeviceNotSet,
#[error("shift Underflow i64(signed long)")]
ShiftUnderflowI64,
#[error("shift Overflow i64(signed long)")]
ShiftOverflowI64,
#[error("shift underflow i32(signed int)")]
ShiftUnderflowI32,
#[error("shift overflow i32(signed int)")]
ShiftOverflowI32,
#[error("duplicate define '{0}'")]
DuplicateDefine(String),
#[error("read only field '{0}'")]
ReadOnlyField(String),
#[error("write only field '{0}'")]
WriteOnlyField(String),
#[error("device has no field '{0}'")]
DeviceHasNoField(String),
#[error("device has no ic")]
DeviceHasNoIC,
#[error("unknown device '{0}'")]
UnknownDeviceId(f64),
#[error("unknown logic type '{0}'")]
UnknownLogicType(f64),
#[error("unknown slot logic type '{0}'")]
UnknownLogicSlotType(f64),
#[error("unknown batch mode '{0}'")]
UnknownBatchMode(f64),
#[error("unknown reagent mode '{0}'")]
UnknownReagentMode(f64),
#[error("type value not known")]
TypeValueNotKnown,
#[error("empty device list")]
EmptyDeviceList,
#[error("connection specifier missing")]
MissingConnectionSpecifier,
#[error("no data network on connection '{0}'")]
NotACableConnection(usize),
#[error("network not connected on connection '{0}'")]
NetworkNotConnected(usize),
#[error("bad network Id '{0}'")]
BadNetworkId(u32),
#[error("channel index out of range '{0}'")]
ChannelIndexOutOfRange(usize),
#[error("slot has no occupant")]
SlotNotOccupied,
#[error("generated Enum {0} has no value attached. Report this error.")]
NoGeneratedValue(String),
#[error(
"generated Enum {enum_name}'s value does not parse as {parse_type} . Report this error."
)]
BadGeneratedValueParse {
enum_name: String,
parse_type: String,
},
#[error("IC with id {0} is not slotted into a circuit holder")]
NoCircuitHolder(ObjectID),
#[error("IC with id {0} is slotted into a circuit holder with no logic interface?")]
CircuitHolderNotLogicable(ObjectID),
#[error("object {0} is not slot writeable")]
NotSlotWriteable(ObjectID),
#[error("object {0} does not use reagents ")]
NotReagentReadable(ObjectID),
#[error("object {0} is not slot logicable")]
NotLogicable(ObjectID),
#[error("{0} is not a valid number of sleep seconds")]
SleepDurationError(f64),
#[error("{duration} can not be added to {time} ")]
SleepAdditionError {
duration: time::Duration,
#[cfg_attr(feature = "tsify", tsify(type = "Date"))]
time: time::OffsetDateTime,
},
}
impl ICError {
pub const fn too_few_operands(provided: usize, desired: u32) -> Self {
ICError::TooFewOperands {
provided: provided as u32,
desired,
}
}
pub const fn too_many_operands(provided: usize, desired: u32) -> Self {
ICError::TooManyOperands {
provided: provided as u32,
desired,
}
}
pub const fn mismatch_operands(provided: usize, desired: u32) -> Self {
if provided < desired as usize {
ICError::too_few_operands(provided, desired)
} else {
ICError::too_many_operands(provided, desired)
}
}
}

View File

@@ -1,144 +1,19 @@
use crate::interpreter::{self, ICError};
use crate::tokens::{SplitConsecutiveIndicesExt, SplitConsecutiveWithIndices};
use crate::{
errors::ParseError,
interpreter,
tokens::{SplitConsecutiveIndicesExt, SplitConsecutiveWithIndices},
vm::instructions::{
enums::InstructionOp,
operands::{Device, DeviceSpec, Identifier, Number, Operand, RegisterSpec},
Instruction, CONSTANTS_LOOKUP,
},
};
use itertools::Itertools;
use std::error::Error;
use std::fmt::Display;
use std::str::FromStr;
use strum::EnumProperty;
pub mod generated {
use super::ParseError;
use crate::interpreter::ICError;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use strum::AsRefStr;
use strum::Display;
use strum::EnumIter;
use strum::EnumProperty;
use strum::EnumString;
use strum::IntoEnumIterator;
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
include!(concat!(env!("OUT_DIR"), "/logictypes.rs"));
include!(concat!(env!("OUT_DIR"), "/modes.rs"));
include!(concat!(env!("OUT_DIR"), "/constants.rs"));
include!(concat!(env!("OUT_DIR"), "/enums.rs"));
impl TryFrom<f64> for LogicType {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <LogicType as TryFrom<f64>>::Error> {
if let Some(lt) = LogicType::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u16>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(lt)
} else {
Err(crate::interpreter::ICError::UnknownLogicType(value))
}
}
}
impl TryFrom<f64> for SlotLogicType {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <SlotLogicType as TryFrom<f64>>::Error> {
if let Some(slt) = SlotLogicType::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(slt)
} else {
Err(crate::interpreter::ICError::UnknownSlotLogicType(value))
}
}
}
impl TryFrom<f64> for BatchMode {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <BatchMode as TryFrom<f64>>::Error> {
if let Some(bm) = BatchMode::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(bm)
} else {
Err(crate::interpreter::ICError::UnknownBatchMode(value))
}
}
}
impl TryFrom<f64> for ReagentMode {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <ReagentMode as TryFrom<f64>>::Error> {
if let Some(rm) = ReagentMode::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(rm)
} else {
Err(crate::interpreter::ICError::UnknownReagentMode(value))
}
}
}
}
pub use generated::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ParseError {
pub line: usize,
pub start: usize,
pub end: usize,
pub msg: String,
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} at line {} {}:{}",
self.msg, self.line, self.start, self.end
)
}
}
impl Error for ParseError {}
impl ParseError {
/// Offset the ParseError in it's line, adding the passed values to it's `start` and `end`
#[must_use]
pub fn offset(self, offset: usize) -> Self {
ParseError {
start: self.start + offset,
end: self.end + offset,
..self
}
}
/// Offset the ParseError line, adding the passed value to it's `line`
#[must_use]
pub fn offset_line(self, offset: usize) -> Self {
ParseError {
line: self.line + offset,
start: self.start,
..self
}
}
/// Mark the parse error as extending 'length' bytes from `start`
#[must_use]
pub fn span(self, length: usize) -> Self {
ParseError {
start: self.start,
end: self.start + length,
..self
}
}
}
use stationeers_data::enums::{
basic::BasicEnum,
script::{LogicBatchMethod, LogicReagentMode, LogicSlotType, LogicType},
};
use std::{fmt::Display, str::FromStr};
pub fn parse(code: &str) -> Result<Vec<Line>, ParseError> {
code.lines()
@@ -148,7 +23,7 @@ pub fn parse(code: &str) -> Result<Vec<Line>, ParseError> {
}
/// Like `parse` but can return Code::Invalid for some lines
pub fn parse_with_invlaid(code: &str) -> Vec<Line> {
pub fn parse_with_invalid(code: &str) -> Vec<Line> {
code.lines()
.enumerate()
.map(|(n, l)| Line::from_str_with_invalid(n, l))
@@ -249,12 +124,6 @@ impl FromStr for Comment {
}
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct Instruction {
pub instruction: InstructionOp,
pub operands: Vec<Operand>,
}
impl FromStr for Instruction {
type Err = ParseError;
/// parse a non-empty string for an instruction and it's operands
@@ -262,9 +131,16 @@ impl FromStr for Instruction {
let mut tokens_iter = s.split_consecutive_with_indices(&[' ', '\t'][..]);
let instruction: InstructionOp = {
if let Some((index, token)) = tokens_iter.next() {
token
.parse::<InstructionOp>()
.map_err(|e| e.offset(index).span(token.len()))
token.parse::<InstructionOp>().map_err(|_e| {
ParseError {
line: 0,
start: 0,
end: 0,
msg: format!("unknown instruction '{token}'"),
}
.offset(index)
.span(token.len())
})
} else {
Err(ParseError {
line: 0,
@@ -323,290 +199,6 @@ fn get_operand_tokens<'a>(
operand_tokens
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Device {
Db,
Numbered(u32),
Indirect { indirection: u32, target: u32 },
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct RegisterSpec {
pub indirection: u32,
pub target: u32,
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct DeviceSpec {
pub device: Device,
pub connection: Option<usize>,
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum Operand {
RegisterSpec(RegisterSpec),
DeviceSpec(DeviceSpec),
Number(Number),
Type {
logic_type: Option<LogicType>,
slot_logic_type: Option<SlotLogicType>,
batch_mode: Option<BatchMode>,
reagent_mode: Option<ReagentMode>,
identifier: Identifier,
},
Identifier(Identifier),
}
impl Operand {
pub fn as_value(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<f64, interpreter::ICError> {
match self.translate_alias(ic) {
Operand::RegisterSpec(RegisterSpec {
indirection,
target,
}) => ic.get_register(indirection, target),
Operand::Number(num) => Ok(num.value()),
Operand::Type {
logic_type,
slot_logic_type,
batch_mode,
reagent_mode,
identifier: _,
} => {
if let Some(lt) = logic_type {
Ok(lt
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(lt.to_string()))?
.parse::<u16>()
.map_err(|_| {
ICError::BadGeneratedValueParse(lt.to_string(), "u16".to_owned())
})? as f64)
} else if let Some(slt) = slot_logic_type {
Ok(slt
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(slt.to_string()))?
.parse::<u8>()
.map_err(|_| {
ICError::BadGeneratedValueParse(slt.to_string(), "u8".to_owned())
})? as f64)
} else if let Some(bm) = batch_mode {
Ok(bm
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(bm.to_string()))?
.parse::<u8>()
.map_err(|_| {
ICError::BadGeneratedValueParse(bm.to_string(), "u8".to_owned())
})? as f64)
} else if let Some(rm) = reagent_mode {
Ok(rm
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(rm.to_string()))?
.parse::<u8>()
.map_err(|_| {
ICError::BadGeneratedValueParse(rm.to_string(), "u8".to_owned())
})? as f64)
} else {
Err(interpreter::ICError::TypeValueNotKnown)
}
}
Operand::Identifier(id) => {
Err(interpreter::ICError::UnknownIdentifier(id.name.to_string()))
}
Operand::DeviceSpec { .. } => Err(interpreter::ICError::IncorrectOperandType {
inst,
index,
desired: "Value".to_owned(),
}),
}
}
pub fn as_value_i64(
&self,
ic: &interpreter::IC,
signed: bool,
inst: InstructionOp,
index: u32,
) -> Result<i64, interpreter::ICError> {
match self {
Self::Number(num) => Ok(num.value_i64(signed)),
_ => {
let val = self.as_value(ic, inst, index)?;
if val < -9.223_372_036_854_776E18 {
Err(interpreter::ICError::ShiftUnderflowI64)
} else if val <= 9.223_372_036_854_776E18 {
Ok(interpreter::f64_to_i64(val, signed))
} else {
Err(interpreter::ICError::ShiftOverflowI64)
}
}
}
}
pub fn as_value_i32(
&self,
ic: &interpreter::IC,
signed: bool,
inst: InstructionOp,
index: u32,
) -> Result<i32, interpreter::ICError> {
match self {
Self::Number(num) => Ok(num.value_i64(signed) as i32),
_ => {
let val = self.as_value(ic, inst, index)?;
if val < -2147483648.0 {
Err(interpreter::ICError::ShiftUnderflowI32)
} else if val <= 2147483647.0 {
Ok(val as i32)
} else {
Err(interpreter::ICError::ShiftOverflowI32)
}
}
}
}
pub fn as_register(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<RegisterSpec, interpreter::ICError> {
match self.translate_alias(ic) {
Operand::RegisterSpec(reg) => Ok(reg),
Operand::Identifier(id) => {
Err(interpreter::ICError::UnknownIdentifier(id.name.to_string()))
}
_ => Err(interpreter::ICError::IncorrectOperandType {
inst,
index,
desired: "Register".to_owned(),
}),
}
}
pub fn as_device(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<(Option<u32>, Option<usize>), interpreter::ICError> {
match self.translate_alias(ic) {
Operand::DeviceSpec(DeviceSpec { device, connection }) => match device {
Device::Db => Ok((Some(ic.device), connection)),
Device::Numbered(p) => {
let dp = ic
.pins
.borrow()
.get(p as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(p as f64))
.copied()?;
Ok((dp, connection))
}
Device::Indirect {
indirection,
target,
} => {
let val = ic.get_register(indirection, target)?;
let dp = ic
.pins
.borrow()
.get(val as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(val))
.copied()?;
Ok((dp, connection))
}
},
Operand::Identifier(id) => {
Err(interpreter::ICError::UnknownIdentifier(id.name.to_string()))
}
_ => Err(interpreter::ICError::IncorrectOperandType {
inst,
index,
desired: "Value".to_owned(),
}),
}
}
pub fn as_logic_type(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<LogicType, ICError> {
match &self {
Operand::Type {
logic_type: Some(lt),
..
} => Ok(*lt),
_ => LogicType::try_from(self.as_value(ic, inst, index)?),
}
}
pub fn as_slot_logic_type(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<SlotLogicType, ICError> {
match &self {
Operand::Type {
slot_logic_type: Some(slt),
..
} => Ok(*slt),
_ => SlotLogicType::try_from(self.as_value(ic, inst, index)?),
}
}
pub fn as_batch_mode(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<BatchMode, ICError> {
match &self {
Operand::Type {
batch_mode: Some(bm),
..
} => Ok(*bm),
_ => BatchMode::try_from(self.as_value(ic, inst, index)?),
}
}
pub fn as_reagent_mode(
&self,
ic: &interpreter::IC,
inst: InstructionOp,
index: u32,
) -> Result<ReagentMode, ICError> {
match &self {
Operand::Type {
reagent_mode: Some(rm),
..
} => Ok(*rm),
_ => ReagentMode::try_from(self.as_value(ic, inst, index)?),
}
}
pub fn translate_alias(&self, ic: &interpreter::IC) -> Self {
match &self {
Operand::Identifier(id) | Operand::Type { identifier: id, .. } => {
if let Some(alias) = ic.aliases.borrow().get(&id.name) {
alias.clone()
} else if let Some(define) = ic.defines.borrow().get(&id.name) {
Operand::Number(Number::Float(*define))
} else if let Some(label) = ic.program.borrow().labels.get(&id.name) {
Operand::Number(Number::Float(*label as f64))
} else {
self.clone()
}
}
_ => self.clone(),
}
}
}
impl FromStr for Operand {
type Err = ParseError;
/// Parse a str containing an single instruction operand
@@ -877,15 +469,13 @@ impl FromStr for Operand {
}
} else if let Some(val) = CONSTANTS_LOOKUP.get(s) {
Ok(Operand::Number(Number::Constant(*val)))
} else if let Ok(val) = LogicEnums::from_str(s) {
Ok(Operand::Number(Number::Enum(
val.get_str("value").unwrap().parse().unwrap(),
)))
} else if let Ok(val) = BasicEnum::from_str(s) {
Ok(Operand::Number(Number::Enum(val.get_value() as f64)))
} else {
let lt = LogicType::from_str(s).ok();
let slt = SlotLogicType::from_str(s).ok();
let bm = BatchMode::from_str(s).ok();
let rm = ReagentMode::from_str(s).ok();
let slt = LogicSlotType::from_str(s).ok();
let bm = LogicBatchMethod::from_str(s).ok();
let rm = LogicReagentMode::from_str(s).ok();
let identifier = Identifier::from_str(s)?;
if lt.is_some() || slt.is_some() || bm.is_some() || rm.is_some() {
Ok(Operand::Type {
@@ -1010,11 +600,6 @@ impl FromStr for Label {
}
}
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub struct Identifier {
pub name: String,
}
impl FromStr for Identifier {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -1060,16 +645,6 @@ impl Display for Identifier {
}
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum Number {
Float(f64),
Binary(i64),
Hexadecimal(i64),
Constant(f64),
String(String),
Enum(f64),
}
impl Number {
pub fn value(&self) -> f64 {
match self {
@@ -1081,7 +656,9 @@ impl Number {
}
pub fn value_i64(&self, signed: bool) -> i64 {
match self {
Number::Enum(val) | Number::Float(val) | Number::Constant(val) => interpreter::f64_to_i64(*val, signed),
Number::Enum(val) | Number::Float(val) | Number::Constant(val) => {
interpreter::f64_to_i64(*val, signed)
}
Number::Binary(val) | Number::Hexadecimal(val) => *val,
Number::String(s) => const_crc32::crc32(s.as_bytes()) as i32 as i64,
}
@@ -1090,10 +667,22 @@ impl Number {
#[cfg(test)]
mod tests {
use color_eyre::eyre::Ok;
use strum::EnumProperty;
use super::*;
static INIT: std::sync::Once = std::sync::Once::new();
fn setup() {
INIT.call_once(|| {
let _ = color_eyre::install();
})
}
#[test]
fn parse_register() {
fn parse_register() -> color_eyre::Result<()> {
setup();
let op = "requestingot".parse::<Operand>();
assert_eq!(
op.unwrap(),
@@ -1101,10 +690,12 @@ mod tests {
name: "requestingot".to_owned()
})
);
Ok(())
}
#[test]
fn successful_parse() {
fn successful_parse() -> color_eyre::Result<()> {
setup();
let parsed = parse("s d0 Setting 0 # This is a comment\n");
dbg!(&parsed);
assert_eq!(
@@ -1152,10 +743,12 @@ mod tests {
comment: None,
},],
);
Ok(())
}
#[test]
fn parse_code_chunk() {
fn parse_code_chunk() -> color_eyre::Result<()> {
setup();
let code = "# This is a comment\n\
define a_def 10\n\
define a_hash HASH(\"This is a String\")\n\
@@ -1250,7 +843,7 @@ mod tests {
}),
Operand::Type {
logic_type: Some(LogicType::On),
slot_logic_type: Some(SlotLogicType::On),
slot_logic_type: Some(LogicSlotType::On),
batch_mode: None,
reagent_mode: None,
identifier: Identifier {
@@ -1425,15 +1018,19 @@ mod tests {
},
],
);
Ok(())
}
#[test]
fn test_operand_display() {
fn test_operand_display() -> color_eyre::Result<()> {
setup();
#[track_caller]
fn test_roundtrip(s: &str) {
let o: Operand = s.parse().expect("test string should parse with FromStr");
assert_eq!(o.to_string(), s);
}
test_roundtrip("r0");
test_roundtrip("r15");
test_roundtrip("rr4");
@@ -1459,9 +1056,9 @@ mod tests {
test_roundtrip("1.2345");
test_roundtrip("-1.2345");
test_roundtrip(LogicType::Pressure.as_ref());
test_roundtrip(SlotLogicType::Occupied.as_ref());
test_roundtrip(BatchMode::Average.as_ref());
test_roundtrip(ReagentMode::Recipe.as_ref());
test_roundtrip(LogicSlotType::Occupied.as_ref());
test_roundtrip(LogicBatchMethod::Average.as_ref());
test_roundtrip(LogicReagentMode::Recipe.as_ref());
test_roundtrip("pi");
test_roundtrip("pinf");
test_roundtrip("ninf");
@@ -1469,48 +1066,58 @@ mod tests {
test_roundtrip(r#"HASH("StructureFurnace")"#);
test_roundtrip("$abcd");
test_roundtrip("%1001");
Ok(())
}
#[test]
fn all_generated_enums_have_value() {
fn all_generated_enums_have_value() -> color_eyre::Result<()> {
setup();
use strum::IntoEnumIterator;
for lt in LogicType::iter() {
println!("testing LogicType.{lt}");
let value = lt.get_str("value");
assert!(value.is_some());
assert!(value.unwrap().parse::<u16>().is_ok());
assert_eq!(lt as u16, value.unwrap().parse::<u16>().unwrap());
}
for slt in SlotLogicType::iter() {
println!("testing SlotLogicType.{slt}");
for slt in LogicSlotType::iter() {
println!("testing LogicSlotType.{slt}");
let value = slt.get_str("value");
assert!(value.is_some());
assert!(value.unwrap().parse::<u8>().is_ok());
assert_eq!(slt as u8, value.unwrap().parse::<u8>().unwrap());
}
for bm in BatchMode::iter() {
for bm in LogicReagentMode::iter() {
println!("testing BatchMode.{bm}");
let value = bm.get_str("value");
assert!(value.is_some());
assert!(value.unwrap().parse::<u8>().is_ok());
assert_eq!(bm as u8, value.unwrap().parse::<u8>().unwrap());
}
for rm in ReagentMode::iter() {
for rm in LogicReagentMode::iter() {
println!("testing ReagentMode.{rm}");
let value = rm.get_str("value");
assert!(value.is_some());
assert!(value.unwrap().parse::<u8>().is_ok());
assert_eq!(rm as u8, value.unwrap().parse::<u8>().unwrap());
}
for le in LogicEnums::iter() {
println!("testing Enum.{le}");
for le in BasicEnum::iter() {
println!("testing BasicEnum {le}");
let value = le.get_str("value");
assert!(value.is_some());
assert!(value.unwrap().parse::<u32>().is_ok());
assert_eq!(le.get_value(), value.unwrap().parse::<u32>().unwrap());
}
Ok(())
}
#[test]
fn bad_parse_does_not_panic() {
fn bad_parse_does_not_panic() -> color_eyre::Result<()> {
setup();
let code = "move foo -";
let parsed = parse(code);
assert!(parsed.is_err());
println!("{}", parsed.unwrap_err());
Ok(())
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,7 @@
pub mod errors;
pub mod grammar;
pub mod interpreter;
pub mod network;
mod rand_mscorlib;
pub mod tokens;
pub mod device;
pub mod vm;
pub mod network;

View File

@@ -1,12 +1,25 @@
use std::{collections::HashSet, ops::Deref};
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, EnumIter};
use thiserror::Error;
use std::{collections::HashSet, ops::Deref, rc::Rc};
use crate::vm::{
object::{errors::LogicError, macros::ObjectInterface, traits::*, Name, ObjectID},
VM,
};
use itertools::Itertools;
use macro_rules_attribute::derive;
use serde_derive::{Deserialize, Serialize};
use stationeers_data::{
enums::{script::LogicType, ConnectionRole, ConnectionType},
templates::ConnectionInfo,
};
use thiserror::Error;
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum CableConnectionType {
Power,
Data,
@@ -14,158 +27,296 @@ pub enum CableConnectionType {
PowerAndData,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum Connection {
CableNetwork {
net: Option<u32>,
net: Option<ObjectID>,
typ: CableConnectionType,
role: ConnectionRole,
},
Chute {
role: ConnectionRole,
},
Pipe {
role: ConnectionRole,
},
Elevator {
role: ConnectionRole,
},
LandingPad {
role: ConnectionRole,
},
LaunchPad {
role: ConnectionRole,
},
PipeLiquid {
role: ConnectionRole,
},
RoboticArmRail {
role: ConnectionRole,
},
#[default]
Other,
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, AsRefStr,
)]
pub enum ConnectionType {
Pipe,
Power,
Data,
Chute,
Elevator,
PipeLiquid,
LandingPad,
LaunchPad,
PowerAndData,
#[serde(other)]
#[default]
None,
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, AsRefStr,
)]
pub enum ConnectionRole {
Input,
Input2,
Output,
Output2,
Waste,
#[serde(other)]
#[default]
None,
}
impl Connection {
#[allow(dead_code)]
fn from(typ: ConnectionType, _role: ConnectionRole) -> Self {
pub fn from_info(typ: ConnectionType, role: ConnectionRole, net: Option<ObjectID>) -> Self {
match typ {
ConnectionType::None
| ConnectionType::Chute
| ConnectionType::Pipe
| ConnectionType::Elevator
| ConnectionType::LandingPad
| ConnectionType::LaunchPad
| ConnectionType::PipeLiquid => Self::Other,
ConnectionType::None => Self::None,
ConnectionType::Data => Self::CableNetwork {
net: None,
net,
typ: CableConnectionType::Data,
role,
},
ConnectionType::Power => Self::CableNetwork {
net: None,
net,
typ: CableConnectionType::Power,
role,
},
ConnectionType::PowerAndData => Self::CableNetwork {
net: None,
net,
typ: CableConnectionType::PowerAndData,
role,
},
ConnectionType::Chute => Self::Chute { role },
ConnectionType::Pipe => Self::Pipe { role },
ConnectionType::Elevator => Self::Elevator { role },
ConnectionType::LandingPad => Self::LandingPad { role },
ConnectionType::LaunchPad => Self::LaunchPad { role },
ConnectionType::PipeLiquid => Self::PipeLiquid { role },
ConnectionType::RoboticArmRail => Self::RoboticArmRail { role },
}
}
pub fn to_info(&self) -> ConnectionInfo {
match self {
Self::None => ConnectionInfo {
typ: ConnectionType::None,
role: ConnectionRole::None,
},
Self::CableNetwork {
typ: CableConnectionType::Data,
role,
..
} => ConnectionInfo {
typ: ConnectionType::Data,
role: *role,
},
Self::CableNetwork {
typ: CableConnectionType::Power,
role,
..
} => ConnectionInfo {
typ: ConnectionType::Power,
role: *role,
},
Self::CableNetwork {
typ: CableConnectionType::PowerAndData,
role,
..
} => ConnectionInfo {
typ: ConnectionType::PowerAndData,
role: *role,
},
Self::Chute { role } => ConnectionInfo {
typ: ConnectionType::Chute,
role: *role,
},
Self::Pipe { role } => ConnectionInfo {
typ: ConnectionType::Pipe,
role: *role,
},
Self::PipeLiquid { role } => ConnectionInfo {
typ: ConnectionType::PipeLiquid,
role: *role,
},
Self::Elevator { role } => ConnectionInfo {
typ: ConnectionType::Elevator,
role: *role,
},
Self::LandingPad { role } => ConnectionInfo {
typ: ConnectionType::LandingPad,
role: *role,
},
Self::LaunchPad { role } => ConnectionInfo {
typ: ConnectionType::LaunchPad,
role: *role,
},
Self::RoboticArmRail { role } => ConnectionInfo {
typ: ConnectionType::RoboticArmRail,
role: *role,
},
}
}
pub fn get_network(&self) -> Option<ObjectID> {
match self {
Self::CableNetwork { net, .. } => *net,
_ => None,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Network {
pub id: u32,
pub devices: HashSet<u32>,
pub power_only: HashSet<u32>,
#[derive(ObjectInterface!, Debug)]
#[custom(implements(Object { Storage, Logicable, Network}))]
pub struct CableNetwork {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
/// required by object interface but atm unused by network
pub prefab: Name,
#[custom(object_name)]
/// required by object interface but atm unused by network
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
/// data enabled objects (must be devices)
pub devices: HashSet<ObjectID>,
/// power only connections
pub power_only: HashSet<ObjectID>,
/// channel data
pub channels: [f64; 8],
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrozenNetwork {
pub id: u32,
pub devices: Vec<u32>,
pub power_only: Vec<u32>,
pub channels: [f64; 8],
}
impl<T> From<T> for FrozenNetwork
where
T: Deref<Target = Network>,
{
fn from(value: T) -> Self {
FrozenNetwork {
id: value.id,
devices: value.devices.iter().copied().collect_vec(),
power_only: value.power_only.iter().copied().collect_vec(),
channels: value.channels,
}
impl Storage for CableNetwork {
fn debug_storage(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn slots_count(&self) -> usize {
0
}
fn get_slot(&self, _index: usize) -> Option<&crate::vm::object::Slot> {
None
}
fn get_slot_mut(&mut self, _index: usize) -> Option<&mut crate::vm::object::Slot> {
None
}
fn get_slots(&self) -> Vec<(usize, &crate::vm::object::Slot)> {
vec![]
}
fn get_slots_mut(&mut self) -> Vec<(usize, &mut crate::vm::object::Slot)> {
vec![]
}
}
impl From<FrozenNetwork> for Network {
fn from(value: FrozenNetwork) -> Self {
Network {
id: value.id,
devices: value.devices.into_iter().collect(),
power_only: value.power_only.into_iter().collect(),
channels: value.channels,
}
impl Logicable for CableNetwork {
fn debug_logicable(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn prefab_hash(&self) -> i32 {
0
}
fn name_hash(&self) -> i32 {
0
}
fn is_logic_readable(&self) -> bool {
true
}
fn is_logic_writeable(&self) -> bool {
true
}
fn can_logic_read(&self, lt: LogicType) -> bool {
use LogicType::*;
matches!(
lt,
Channel0 | Channel1 | Channel2 | Channel3 | Channel4 | Channel5 | Channel6 | Channel7
)
}
fn can_logic_write(&self, lt: LogicType) -> bool {
use LogicType::*;
matches!(
lt,
Channel0 | Channel1 | Channel2 | Channel3 | Channel4 | Channel5 | Channel6 | Channel7
)
}
fn get_logic(&self, lt: LogicType) -> Result<f64, crate::vm::object::errors::LogicError> {
use LogicType::*;
let index: usize = match lt {
Channel0 => 0,
Channel1 => 1,
Channel2 => 2,
Channel3 => 3,
Channel4 => 4,
Channel5 => 5,
Channel6 => 6,
Channel7 => 7,
_ => return Err(LogicError::CantRead(lt)),
};
Ok(self.channels[index])
}
fn set_logic(&mut self, lt: LogicType, value: f64, _force: bool) -> Result<(), LogicError> {
use LogicType::*;
let index: usize = match lt {
Channel0 => 0,
Channel1 => 1,
Channel2 => 2,
Channel3 => 3,
Channel4 => 4,
Channel5 => 5,
Channel6 => 6,
Channel7 => 7,
_ => return Err(LogicError::CantWrite(lt)),
};
self.channels[index] = value;
Ok(())
}
fn can_slot_logic_read(
&self,
_slt: stationeers_data::enums::script::LogicSlotType,
_index: f64,
) -> bool {
false
}
fn get_slot_logic(
&self,
slt: stationeers_data::enums::script::LogicSlotType,
index: f64,
) -> Result<f64, LogicError> {
Err(LogicError::CantSlotRead(slt, index))
}
fn valid_logic_types(&self) -> Vec<LogicType> {
use LogicType::*;
vec![
Channel0, Channel1, Channel2, Channel3, Channel4, Channel5, Channel6, Channel7,
]
}
fn known_modes(&self) -> Option<Vec<(u32, String)>> {
None
}
}
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("")]
ChannelIndexOutOfRange,
}
impl Network {
pub fn new(id: u32) -> Self {
Network {
id,
devices: HashSet::new(),
power_only: HashSet::new(),
channels: [f64::NAN; 8],
}
impl Network for CableNetwork {
fn debug_network(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
pub fn contains(&self, id: &u32) -> bool {
fn contains(&self, id: &ObjectID) -> bool {
self.devices.contains(id) || self.power_only.contains(id)
}
pub fn contains_all(&self, ids: &[u32]) -> bool {
fn contains_all(&self, ids: &[ObjectID]) -> bool {
ids.iter().all(|id| self.contains(id))
}
pub fn contains_data(&self, id: &u32) -> bool {
fn contains_data(&self, id: &ObjectID) -> bool {
self.devices.contains(id)
}
pub fn contains_all_data(&self, ids: &[u32]) -> bool {
fn contains_all_data(&self, ids: &[ObjectID]) -> bool {
ids.iter().all(|id| self.contains_data(id))
}
pub fn contains_power(&self, id: &u32) -> bool {
fn contains_power(&self, id: &ObjectID) -> bool {
self.power_only.contains(id)
}
pub fn contains_all_power(&self, ids: &[u32]) -> bool {
fn contains_all_power(&self, ids: &[ObjectID]) -> bool {
ids.iter().all(|id| self.contains_power(id))
}
pub fn data_visible(&self, source: &u32) -> Vec<u32> {
fn data_visible(&self, source: &ObjectID) -> Vec<ObjectID> {
if self.contains_data(source) {
self.devices
.iter()
@@ -177,25 +328,102 @@ impl Network {
}
}
pub fn add_data(&mut self, id: u32) -> bool {
fn add_data(&mut self, id: ObjectID) -> bool {
self.devices.insert(id)
}
pub fn add_power(&mut self, id: u32) -> bool {
fn add_power(&mut self, id: ObjectID) -> bool {
self.power_only.insert(id)
}
pub fn remove_all(&mut self, id: u32) -> bool {
fn remove_all(&mut self, id: ObjectID) -> bool {
self.devices.remove(&id) || self.power_only.remove(&id)
}
pub fn remove_data(&mut self, id: u32) -> bool {
fn remove_data(&mut self, id: ObjectID) -> bool {
self.devices.remove(&id)
}
pub fn remove_power(&mut self, id: u32) -> bool {
fn remove_power(&mut self, id: ObjectID) -> bool {
self.devices.remove(&id)
}
fn get_devices(&self) -> Vec<ObjectID> {
self.devices.iter().copied().collect_vec()
}
fn get_power_only(&self) -> Vec<ObjectID> {
self.power_only.iter().copied().collect_vec()
}
fn get_channel_data(&self) -> &[f64; 8] {
&self.channels
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct FrozenCableNetwork {
pub id: ObjectID,
pub devices: Vec<u32>,
pub power_only: Vec<u32>,
pub channels: [f64; 8],
}
impl<T> From<T> for FrozenCableNetwork
where
T: Deref<Target = CableNetwork>,
{
fn from(value: T) -> Self {
FrozenCableNetwork {
id: value.id,
devices: value.devices.iter().copied().collect_vec(),
power_only: value.power_only.iter().copied().collect_vec(),
channels: value.channels,
}
}
}
impl From<NetworkRef<'_>> for FrozenCableNetwork {
fn from(value: NetworkRef) -> Self {
FrozenCableNetwork {
id: *value.get_id(),
devices: value.get_devices(),
power_only: value.get_power_only(),
channels: *value.get_channel_data(),
}
}
}
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("")]
ChannelIndexOutOfRange,
}
impl CableNetwork {
pub fn new(id: u32, vm: Rc<VM>) -> Self {
CableNetwork {
id,
prefab: Name::new(""),
name: Name::new(""),
vm,
devices: HashSet::new(),
power_only: HashSet::new(),
channels: [f64::NAN; 8],
}
}
pub fn from_frozen(value: FrozenCableNetwork, vm: Rc<VM>) -> Self {
CableNetwork {
id: value.id,
prefab: Name::new(""),
name: Name::new(""),
vm,
devices: value.devices.into_iter().collect(),
power_only: value.power_only.into_iter().collect(),
channels: value.channels,
}
}
pub fn set_channel(&mut self, chan: usize, val: f64) -> Result<f64, NetworkError> {
if chan > 7 {
Err(NetworkError::ChannelIndexOutOfRange)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
mod codegen;
pub mod operands;
pub use codegen::enums;
pub use codegen::traits;
use enums::InstructionOp;
use operands::Operand;
use serde_derive::{Deserialize, Serialize};
use phf::phf_map;
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct Instruction {
pub instruction: InstructionOp,
pub operands: Vec<Operand>,
}
#[allow(clippy::approx_constant)]
pub static CONSTANTS_LOOKUP: phf::Map<&'static str, f64> = phf_map! {
"nan" => f64::NAN,
"ninf" => f64::NEG_INFINITY,
"deg2rad" => 0.0174532923847437f64,
"rad2deg" => 57.2957801818848f64,
"epsilon" => f64::EPSILON,
"pinf" => f64::INFINITY,
"pi" => 3.141592653589793f64,
};

View File

@@ -0,0 +1,2 @@
pub mod traits;
pub mod enums;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,336 @@
use crate::errors::ICError;
use crate::interpreter;
use crate::vm::{instructions::enums::InstructionOp, object::traits::IntegratedCircuit};
use serde_derive::{Deserialize, Serialize};
use stationeers_data::enums::script::{
LogicBatchMethod, LogicReagentMode, LogicSlotType, LogicType,
};
use strum::EnumProperty;
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum Device {
Db,
Numbered(u32),
Indirect { indirection: u32, target: u32 },
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct RegisterSpec {
pub indirection: u32,
pub target: u32,
}
#[serde_with::skip_serializing_none]
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct DeviceSpec {
pub device: Device,
pub connection: Option<usize>,
}
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Identifier {
pub name: String,
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum Number {
Float(f64),
Binary(i64),
Hexadecimal(i64),
Constant(f64),
String(String),
Enum(f64),
}
#[serde_with::skip_serializing_none]
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum Operand {
RegisterSpec(RegisterSpec),
DeviceSpec(DeviceSpec),
Number(Number),
Type {
logic_type: Option<LogicType>,
slot_logic_type: Option<LogicSlotType>,
batch_mode: Option<LogicBatchMethod>,
reagent_mode: Option<LogicReagentMode>,
identifier: Identifier,
},
Identifier(Identifier),
}
pub struct InstOperand {
pub operand: Operand,
pub inst: InstructionOp,
pub index: usize,
}
impl InstOperand {
pub fn new(operand: &Operand, inst: InstructionOp, index: usize) -> Self {
InstOperand {
operand: operand.clone(),
inst,
index,
}
}
pub fn as_ident(&self) -> Result<Identifier, ICError> {
let Operand::Identifier(ident) = &self.operand else {
return Err(ICError::IncorrectOperandType {
inst: self.inst,
index: self.index,
desired: "Name".to_owned(),
});
};
Ok(ident.clone())
}
pub fn as_number(&self) -> Result<Number, ICError> {
let Operand::Number(num) = &self.operand else {
return Err(ICError::IncorrectOperandType {
inst: self.inst,
index: self.index,
desired: "Number".to_owned(),
});
};
Ok(num.clone())
}
pub fn as_aliasable(&self) -> Result<Operand, ICError> {
match &self.operand {
Operand::RegisterSpec { .. } | Operand::DeviceSpec { .. } => Ok(self.operand.clone()),
_ => Err(ICError::IncorrectOperandType {
inst: self.inst,
index: self.index,
desired: "Device Or Register".to_owned(),
}),
}
}
pub fn as_value<IC: IntegratedCircuit>(&self, ic: &IC) -> Result<f64, ICError> {
match self.translate_alias(ic) {
Operand::RegisterSpec(RegisterSpec {
indirection,
target,
}) => ic.get_register(indirection, target),
Operand::Number(num) => Ok(num.value()),
Operand::Type {
logic_type,
slot_logic_type,
batch_mode,
reagent_mode,
identifier: _,
} => {
if let Some(lt) = logic_type {
Ok(lt
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(lt.to_string()))?
.parse::<u16>()
.map_err(|_| ICError::BadGeneratedValueParse {
enum_name: lt.to_string(),
parse_type: "u16".to_owned(),
})? as f64)
} else if let Some(slt) = slot_logic_type {
Ok(slt
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(slt.to_string()))?
.parse::<u8>()
.map_err(|_| ICError::BadGeneratedValueParse {
enum_name: slt.to_string(),
parse_type: "u8".to_owned(),
})? as f64)
} else if let Some(bm) = batch_mode {
Ok(bm
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(bm.to_string()))?
.parse::<u8>()
.map_err(|_| ICError::BadGeneratedValueParse {
enum_name: bm.to_string(),
parse_type: "u8".to_owned(),
})? as f64)
} else if let Some(rm) = reagent_mode {
Ok(rm
.get_str("value")
.ok_or_else(|| ICError::NoGeneratedValue(rm.to_string()))?
.parse::<u8>()
.map_err(|_| ICError::BadGeneratedValueParse {
enum_name: rm.to_string(),
parse_type: "u8".to_owned(),
})? as f64)
} else {
Err(ICError::TypeValueNotKnown)
}
}
Operand::Identifier(id) => Err(ICError::UnknownIdentifier(id.name.to_string())),
Operand::DeviceSpec { .. } => Err(ICError::IncorrectOperandType {
inst: self.inst,
index: self.index,
desired: "Value".to_owned(),
}),
}
}
pub fn as_value_i64<IC: IntegratedCircuit>(
&self,
ic: &IC,
signed: bool,
) -> Result<i64, ICError> {
match &self.operand {
Operand::Number(num) => Ok(num.value_i64(signed)),
_ => {
let val = self.as_value(ic)?;
if val < i64::MIN as f64 {
Err(ICError::ShiftUnderflowI64)
} else if val <= i64::MAX as f64 {
Ok(interpreter::f64_to_i64(val, signed))
} else {
Err(ICError::ShiftOverflowI64)
}
}
}
}
pub fn as_value_i32<IC: IntegratedCircuit>(
&self,
ic: &IC,
signed: bool,
) -> Result<i32, ICError> {
match &self.operand {
Operand::Number(num) => Ok(num.value_i64(signed) as i32),
_ => {
let val = self.as_value(ic)?;
if val < i32::MIN as f64 {
Err(ICError::ShiftUnderflowI32)
} else if val <= i32::MAX as f64 {
Ok(val as i32)
} else {
Err(ICError::ShiftOverflowI32)
}
}
}
}
pub fn as_register<IC: IntegratedCircuit>(&self, ic: &IC) -> Result<RegisterSpec, ICError> {
match self.translate_alias(ic) {
Operand::RegisterSpec(reg) => Ok(reg),
Operand::Identifier(id) => Err(ICError::UnknownIdentifier(id.name.to_string())),
_ => Err(ICError::IncorrectOperandType {
inst: self.inst,
index: self.index,
desired: "Register".to_owned(),
}),
}
}
/// interpret the operand as a device index, i32::MAX is db
pub fn as_device<IC: IntegratedCircuit>(
&self,
ic: &IC,
) -> Result<(i32, Option<usize>), ICError> {
match self.translate_alias(ic) {
Operand::DeviceSpec(DeviceSpec { device, connection }) => match device {
Device::Db => Ok((i32::MAX, connection)),
Device::Numbered(p) => Ok((p as i32, connection)),
Device::Indirect {
indirection,
target,
} => {
let val = ic.get_register(indirection, target)?;
Ok((val as i32, connection))
}
},
Operand::Identifier(id) => Err(ICError::UnknownIdentifier(id.name.to_string())),
_ => Err(ICError::IncorrectOperandType {
inst: self.inst,
index: self.index,
desired: "Value".to_owned(),
}),
}
}
pub fn as_logic_type<IC: IntegratedCircuit>(&self, ic: &IC) -> Result<LogicType, ICError> {
match &self.operand {
Operand::Type {
logic_type: Some(lt),
..
} => Ok(*lt),
_ => {
let val = self.as_value(ic)?;
LogicType::try_from(val).map_err(|_| ICError::UnknownLogicType(val))
}
}
}
pub fn as_slot_logic_type<IC: IntegratedCircuit>(
&self,
ic: &IC,
) -> Result<LogicSlotType, ICError> {
match &self.operand {
Operand::Type {
slot_logic_type: Some(slt),
..
} => Ok(*slt),
_ => {
let val = self.as_value(ic)?;
LogicSlotType::try_from(val).map_err(|_| ICError::UnknownLogicSlotType(val))
}
}
}
pub fn as_batch_mode<IC: IntegratedCircuit>(
&self,
ic: &IC,
) -> Result<LogicBatchMethod, ICError> {
match &self.operand {
Operand::Type {
batch_mode: Some(bm),
..
} => Ok(*bm),
_ => {
let val = self.as_value(ic)?;
LogicBatchMethod::try_from(val).map_err(|_| ICError::UnknownBatchMode(val))
}
}
}
pub fn as_reagent_mode<IC: IntegratedCircuit>(
&self,
ic: &IC,
) -> Result<LogicReagentMode, ICError> {
match &self.operand {
Operand::Type {
reagent_mode: Some(rm),
..
} => Ok(*rm),
_ => {
let val = self.as_value(ic)?;
LogicReagentMode::try_from(val).map_err(|_| ICError::UnknownReagentMode(val))
}
}
}
pub fn translate_alias<IC: IntegratedCircuit>(&self, ic: &IC) -> Operand {
match &self.operand {
Operand::Identifier(id) | Operand::Type { identifier: id, .. } => {
if let Some(alias) = ic.get_aliases().get(&id.name) {
alias.clone()
} else if let Some(define) = ic.get_defines().get(&id.name) {
Operand::Number(Number::Float(*define))
} else if let Some(label) = ic.get_labels().get(&id.name) {
Operand::Number(Number::Float(*label as f64))
} else {
self.operand.clone()
}
}
_ => self.operand.clone(),
}
}
}

164
ic10emu/src/vm/object.rs Normal file
View File

@@ -0,0 +1,164 @@
use std::{
cell::RefCell,
ops::{Deref, DerefMut},
rc::Rc,
};
use macro_rules_attribute::derive;
use serde_derive::{Deserialize, Serialize};
pub mod errors;
pub mod generic;
pub mod humans;
pub mod macros;
pub mod stationpedia;
pub mod templates;
pub mod traits;
use traits::Object;
use crate::vm::VM;
#[cfg(feature = "tsify")]
use tsify::{declare, Tsify};
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
use stationeers_data::enums::{
basic::Class, prefabs::StationpediaPrefab, script::LogicSlotType, MemoryAccess,
};
#[cfg_attr(feature = "tsify", declare)]
pub type ObjectID = u32;
pub type BoxedObject = Rc<RefCell<dyn Object>>;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "tsify", wasm_bindgen)]
pub struct VMObject(BoxedObject);
impl Deref for VMObject {
type Target = BoxedObject;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for VMObject {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl VMObject {
pub fn new<T>(val: T) -> Self
where
T: Object + 'static,
{
VMObject(Rc::new(RefCell::new(val)))
}
pub fn set_vm(&mut self, vm: Rc<VM>) {
self.borrow_mut().set_vm(vm);
}
pub fn get_vm(&self) -> Rc<VM> {
self.borrow().get_vm().clone()
}
pub fn get_id(&self) -> ObjectID {
*self.borrow().get_id()
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Name {
pub value: String,
pub hash: i32,
}
#[allow(unused)]
impl Name {
pub fn new(name: &str) -> Self {
Name {
value: name.to_owned(),
hash: const_crc32::crc32(name.as_bytes()) as i32,
}
}
pub fn from_prefab_name(name: &str) -> Self {
Name {
value: name.to_string(),
hash: name
.parse::<StationpediaPrefab>()
.map(|prefab| prefab as i32)
.unwrap_or_else(|_| const_crc32::crc32(name.as_bytes()) as i32),
}
}
pub fn from_prefab_hash(hash: i32) -> Option<Self> {
StationpediaPrefab::from_repr(hash).map(|prefab| Name {
value: prefab.to_string(),
hash,
})
}
pub fn set(&mut self, name: &str) {
self.value = name.to_owned();
self.hash = const_crc32::crc32(name.as_bytes()) as i32;
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct LogicField {
pub field_type: MemoryAccess,
pub value: f64,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct SlotOccupantInfo {
pub quantity: u32,
pub id: ObjectID,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Slot {
pub parent: ObjectID,
pub index: usize,
pub name: String,
pub class: Class,
pub readable_logic: Vec<LogicSlotType>,
pub writeable_logic: Vec<LogicSlotType>,
pub occupant: Option<SlotOccupantInfo>,
pub proxy: bool,
}
impl Slot {
#[must_use]
pub fn new(parent: ObjectID, index: usize, name: String, class: Class) -> Self {
Slot {
parent,
index,
name,
class,
readable_logic:
vec![
LogicSlotType::Class,
LogicSlotType::Damage,
LogicSlotType::MaxQuantity,
LogicSlotType::OccupantHash,
LogicSlotType::Occupied,
LogicSlotType::PrefabHash,
LogicSlotType::Quantity,
LogicSlotType::ReferenceId,
LogicSlotType::SortingClass,
],
writeable_logic: vec![],
occupant: None,
proxy: false,
}
}
}

View File

@@ -0,0 +1,36 @@
use serde_derive::{Deserialize, Serialize};
use thiserror::Error;
use stationeers_data::enums::script::{LogicSlotType, LogicType};
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum LogicError {
#[error("can't read LogicType {0}")]
CantRead(LogicType),
#[error("can't read slot {1} LogicSlotType {0}")]
CantSlotRead(LogicSlotType, f64),
#[error("can't write LogicType {0}")]
CantWrite(LogicType),
#[error("can't write slot {1} LogicSlotType {0}")]
CantSlotWrite(LogicSlotType, f64),
#[error("slot id {0} is out of range 0..{1}")]
SlotIndexOutOfRange(f64, usize),
}
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum MemoryError {
#[error("stack underflow: {0} < range [0..{1})")]
StackUnderflow(i32, usize),
#[error("stack overflow: {0} > range [0..{1})")]
StackOverflow(i32, usize),
#[error("memory not readable")]
NotReadable,
#[error("memory not writeable")]
NotWriteable,
}

View File

@@ -0,0 +1,3 @@
pub mod macros;
pub mod structs;
pub mod traits;

View File

@@ -0,0 +1,328 @@
macro_rules! GWThermal {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWThermal for $struct {
fn is_thermal(&self) -> bool {
self.thermal_info.is_some()
}
fn thermal_info(&self) -> &ThermalInfo {
self.thermal_info
.as_ref()
.expect("GWThermal::thermal_info called on non thermal")
}
}
};
}
pub(crate) use GWThermal;
macro_rules! GWInternalAtmo {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWInternalAtmo for $struct {
fn is_internal_atmo(&self) -> bool {
self.internal_atmo_info.is_some()
}
fn internal_atmo_info(&self) -> &InternalAtmoInfo {
self.internal_atmo_info
.as_ref()
.expect("GWInternalAtmo::internal_atmo_info called on non internal atmo")
}
}
};
}
pub(crate) use GWInternalAtmo;
macro_rules! GWStructure {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWStructure for $struct {
fn small_grid(&self) -> bool {
self.small_grid
}
}
};
}
pub(crate) use GWStructure;
macro_rules! GWStorage {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWStorage for $struct {
fn slots(&self) -> &BTreeMap<u32, Slot> {
&self.slots
}
fn slots_mut(&mut self) -> &mut BTreeMap<u32, Slot> {
&mut self.slots
}
}
};
}
pub(crate) use GWStorage;
macro_rules! GWLogicable {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWLogicable for $struct {
fn fields(&self) -> &BTreeMap<LogicType, LogicField> {
&self.fields
}
fn fields_mut(&mut self) -> &mut BTreeMap<LogicType, LogicField> {
&mut self.fields
}
fn known_modes(&self) -> Option<&BTreeMap<u32, String>> {
self.modes.as_ref()
}
}
};
}
pub(crate) use GWLogicable;
macro_rules! GWMemoryReadable {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWMemoryReadable for $struct {
fn memory_size(&self) -> usize {
self.memory.len()
}
fn memory(&self) -> &Vec<f64> {
&self.memory
}
}
};
}
pub(crate) use GWMemoryReadable;
macro_rules! GWMemoryWritable {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWMemoryWritable for $struct {
fn memory_mut(&mut self) -> &mut Vec<f64> {
&mut self.memory
}
}
};
}
pub(crate) use GWMemoryWritable;
macro_rules! GWDevice {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWDevice for $struct {
fn device_info(&self) -> &DeviceInfo {
&self.device_info
}
fn connections(&self) -> &[Connection] {
self.connections.as_slice()
}
fn connections_mut(&mut self) -> &mut [Connection] {
self.connections.as_mut_slice()
}
fn pins(&self) -> Option<&[Option<ObjectID>]> {
self.pins.as_ref().map(|pins| pins.as_slice())
}
fn pins_mut(&mut self) -> Option<&mut [Option<ObjectID>]> {
self.pins.as_mut().map(|pins| pins.as_mut_slice())
}
fn reagents(&self) -> Option<&BTreeMap<u8, f64>> {
self.reagents.as_ref()
}
fn reagents_mut(&mut self) -> &mut Option<BTreeMap<u8, f64>> {
&mut self.reagents
}
}
};
}
pub(crate) use GWDevice;
macro_rules! GWItem {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWItem for $struct {
fn item_info(&self) -> &ItemInfo {
&self.item_info
}
fn parent_slot(&self) -> Option<ParentSlotInfo> {
self.parent_slot
}
fn set_parent_slot(&mut self, info: Option<ParentSlotInfo>) {
self.parent_slot = info;
}
fn damage(&self) -> &Option<f32> {
&self.damage
}
fn damage_mut(&mut self) -> &mut Option<f32> {
&mut self.damage
}
}
};
}
pub(crate) use GWItem;
macro_rules! GWSuit {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWSuit for $struct {
fn suit_info(&self) -> &SuitInfo {
&self.suit_info
}
}
};
}
pub(crate) use GWSuit;
macro_rules! GWCircuitHolderItem {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWCircuitHolder for $struct {
type Holder = ItemCircuitHolder;
fn gw_get_error(&self) -> i32 {
self.error
}
fn gw_set_error(&mut self, state: i32) {
self.error = state;
}
}
};
}
pub(crate) use GWCircuitHolderItem;
macro_rules! GWCircuitHolderSuit {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWCircuitHolder for $struct {
type Holder = SuitCircuitHolder;
fn gw_get_error(&self) -> i32 {
self.error
}
fn gw_set_error(&mut self, state: i32) {
self.error = state;
}
}
};
}
pub(crate) use GWCircuitHolderSuit;
macro_rules! GWCircuitHolderDevice {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWCircuitHolder for $struct {
type Holder = DeviceCircuitHolder;
fn gw_get_error(&self) -> i32 {
self.error
}
fn gw_set_error(&mut self, state: i32) {
self.error = state;
}
}
};
}
pub(crate) use GWCircuitHolderDevice;
macro_rules! GWReagentConsumer {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWReagentConsumer for $struct {
fn consumer_info(&self) -> &ConsumerInfo {
&self.consumer_info
}
}
};
}
pub(crate) use GWReagentConsumer;
macro_rules! GWReagentRequirer {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWReagentRequirer for $struct {
fn get_current_recipe_gw(&self) -> Option<(u32, u32)> {
self.current_recipe
}
fn get_fab_info_gw(&self) -> Option<&FabricatorInfo> {
self.fabricator_info.as_ref()
}
}
};
}
pub(crate) use GWReagentRequirer;
macro_rules! GWFabricator {
(
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$($body:tt)*
}
) => {
impl GWFabricator for $struct {
fn is_fabricator(&self) -> bool {
self.fabricator_info.is_some()
}
fn fabricator_info(&self) -> &FabricatorInfo {
self.fabricator_info
.as_ref()
.expect("GWFabricator::fabricator_info call on non Fabricator")
}
}
};
}
pub(crate) use GWFabricator;

View File

@@ -0,0 +1,621 @@
use super::{macros::*, traits::*};
use crate::{
network::Connection,
vm::{
object::{macros::ObjectInterface, traits::*, LogicField, Name, ObjectID, Slot},
VM,
},
};
use macro_rules_attribute::derive;
use stationeers_data::{
enums::script::LogicType,
templates::{
ConsumerInfo, DeviceInfo, FabricatorInfo, InternalAtmoInfo, ItemInfo, SuitInfo, ThermalInfo,
},
};
use std::{collections::BTreeMap, rc::Rc};
#[derive(ObjectInterface!, GWThermal!, GWInternalAtmo!, GWStructure!)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure
}))]
pub struct Generic {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
}
#[derive(ObjectInterface!, GWThermal!, GWInternalAtmo!, GWStructure!, GWStorage!)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage
}))]
pub struct GenericStorage {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!, GWLogicable!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable
}))]
pub struct GenericLogicable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!, GWLogicable!,
GWDevice!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device
}))]
pub struct GenericLogicableDevice {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!, GWLogicable!,
GWDevice!, GWCircuitHolderDevice!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device,
CircuitHolder
}))]
pub struct GenericCircuitHolder {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
pub error: i32,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!, GWLogicable!,
GWDevice!, GWReagentConsumer!,
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device, ReagentConsumer
}))]
pub struct GenericLogicableDeviceConsumer {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
pub consumer_info: ConsumerInfo,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!,
GWLogicable!, GWDevice!,
GWMemoryReadable!, GWMemoryWritable!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device, MemoryReadable
}))]
pub struct GenericLogicableDeviceMemoryReadable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
pub memory: Vec<f64>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!, GWLogicable!,
GWDevice!, GWMemoryReadable!, GWMemoryWritable!,
GWReagentConsumer!, GWReagentRequirer!, GWFabricator!,
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device, MemoryReadable,
ReagentConsumer, ReagentRequirer,
Fabricator[GWFabricator::is_fabricator]
}))]
pub struct GenericLogicableDeviceConsumerMemoryReadable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
pub consumer_info: ConsumerInfo,
pub fabricator_info: Option<FabricatorInfo>,
/// (fabricator_info.recipes index, quantity)
pub current_recipe: Option<(u32, u32)>,
pub memory: Vec<f64>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!,
GWLogicable!, GWDevice!, GWMemoryReadable!, GWMemoryWritable!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device, MemoryReadable, MemoryWritable
}))]
pub struct GenericLogicableDeviceMemoryReadWriteable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
pub memory: Vec<f64>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWStructure!, GWStorage!, GWLogicable!,
GWDevice!, GWMemoryReadable!, GWMemoryWritable!,
GWReagentConsumer!, GWReagentRequirer!, GWFabricator!,
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Structure, Storage, Logicable, Device, MemoryReadable, MemoryWritable,
ReagentConsumer, ReagentRequirer,
Fabricator[GWFabricator::is_fabricator]
}))]
pub struct GenericLogicableDeviceConsumerMemoryReadWriteable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub small_grid: bool,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub device_info: DeviceInfo,
pub connections: Vec<Connection>,
pub pins: Option<Vec<Option<ObjectID>>>,
pub reagents: Option<BTreeMap<u8, f64>>,
pub consumer_info: ConsumerInfo,
pub fabricator_info: Option<FabricatorInfo>,
// index of target recipe in fabricator_info
pub current_recipe: Option<(u32, u32)>,
pub memory: Vec<f64>,
}
#[derive(ObjectInterface!, GWThermal!, GWInternalAtmo!, GWItem!)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item
}))]
pub struct GenericItem {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
}
#[derive(ObjectInterface!, GWThermal!, GWInternalAtmo!, GWItem!, GWStorage! )]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage
}))]
pub struct GenericItemStorage {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
}
#[derive(
ObjectInterface!, GWThermal!,
GWInternalAtmo!, GWItem!, GWStorage!,
GWReagentConsumer!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, ReagentConsumer
}))]
pub struct GenericItemConsumer {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub consumer_info: ConsumerInfo,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWLogicable!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Logicable
}))]
pub struct GenericItemLogicable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWLogicable!,
GWMemoryReadable!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Logicable, MemoryReadable
}))]
pub struct GenericItemLogicableMemoryReadable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub memory: Vec<f64>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWLogicable!,
GWMemoryReadable!, GWMemoryWritable!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Logicable, MemoryReadable, MemoryWritable
}))]
pub struct GenericItemLogicableMemoryReadWriteable {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub memory: Vec<f64>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWLogicable!,
GWCircuitHolderItem!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Logicable,
CircuitHolder
}))]
pub struct GenericItemCircuitHolder {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub error: i32,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWLogicable!,
GWSuit!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Suit, Logicable
}))]
pub struct GenericItemSuitLogic {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub suit_info: SuitInfo,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWLogicable!,
GWMemoryReadable!, GWMemoryWritable!,
GWSuit!, GWCircuitHolderSuit!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Suit, Logicable, MemoryReadable, MemoryWritable,
CircuitHolder
}))]
pub struct GenericItemSuitCircuitHolder {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub suit_info: SuitInfo,
pub fields: BTreeMap<LogicType, LogicField>,
pub modes: Option<BTreeMap<u32, String>>,
pub memory: Vec<f64>,
pub error: i32,
}
#[derive(
ObjectInterface!,
GWThermal!, GWInternalAtmo!,
GWItem!, GWStorage!, GWSuit!
)]
#[custom(implements(Object {
Thermal[GWThermal::is_thermal],
InternalAtmosphere[GWInternalAtmo::is_internal_atmo],
Item, Storage, Suit
}))]
pub struct GenericItemSuit {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub thermal_info: Option<ThermalInfo>,
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub item_info: ItemInfo,
pub parent_slot: Option<ParentSlotInfo>,
pub damage: Option<f32>,
pub slots: BTreeMap<u32, Slot>,
pub suit_info: SuitInfo,
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,423 @@
use std::collections::BTreeMap;
use macro_rules_attribute::derive;
use stationeers_data::enums::{basic::Class, Species};
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
use crate::vm::{
object::{
macros::ObjectInterface,
traits::{
Human, HumanRef, HumanRefMut, Object, StatState, Storage, StorageRef, StorageRefMut,
Thermal,
},
Name, ObjectID, Slot, SlotOccupantInfo,
},
VM,
};
static MAX_NUTRITION: f32 = 50.0;
// static FULL_NUTRITION: f32 = 45.0;
static WARNING_NUTRITION: f32 = 15.0;
static CRITICAL_NUTRITION: f32 = 5.0;
static MAX_HYDRATION: f32 = 8.75;
static WARNING_HYDRATION: f32 = 2.0;
static CRITICAL_HYDRATION: f32 = 1.0;
static MAX_OXYGENATION: f32 = 0.024;
static MAX_FOOD_QUALITY: f32 = 1.0;
static MAX_MOOD: f32 = 1.0;
static WARNING_MOOD: f32 = 0.5;
static CRITICAL_MOOD: f32 = 0.0;
static MAX_HYGIENE: f32 = 1.25;
static WARNING_HYGIENE: f32 = 0.25;
static CRITICAL_HYGIENE: f32 = 0.0;
use serde_derive::{Deserialize, Serialize};
#[derive(ObjectInterface!)]
#[custom(implements(Object {
Human, Storage
}))]
pub struct HumanPlayer {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: std::rc::Rc<VM>,
pub species: Species,
pub damage: f32,
pub hydration: f32,
pub nutrition: f32,
pub oxygenation: f32,
pub food_quality: f32,
pub mood: f32,
pub hygiene: f32,
left_hand_slot: Slot,
right_hand_slot: Slot,
suit_slot: Slot,
helmet_slot: Slot,
glasses_slot: Slot,
backpack_slot: Slot,
uniform_slot: Slot,
toolbelt_slot: Slot,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct EntityInfo {
pub hydration: f32,
pub nutrition: f32,
pub oxygenation: f32,
pub food_quality: f32,
pub mood: f32,
pub hygiene: f32,
}
impl HumanPlayer {
pub fn new(id: ObjectID, vm: std::rc::Rc<VM>) -> Self {
HumanPlayer {
id,
prefab: Name::new(""),
name: Name::new(""),
vm,
species: Species::Human,
damage: 0.0,
hydration: 5.0,
nutrition: 50.0,
oxygenation: 0.024,
food_quality: 0.75,
mood: 1.0,
hygiene: 1.0,
left_hand_slot: Slot::new(id, 0, "LeftHand".to_string(), Class::None),
right_hand_slot: Slot::new(id, 1, "RightHand".to_string(), Class::None),
suit_slot: Slot::new(id, 2, "Suit".to_string(), Class::Suit),
helmet_slot: Slot::new(id, 3, "Helmet".to_string(), Class::Helmet),
glasses_slot: Slot::new(id, 4, "Glasses".to_string(), Class::Glasses),
backpack_slot: Slot::new(id, 5, "Back".to_string(), Class::Back),
uniform_slot: Slot::new(id, 6, "Uniform".to_string(), Class::Uniform),
toolbelt_slot: Slot::new(id, 7, "Belt".to_string(), Class::Belt),
}
}
pub fn with_species(id: ObjectID, vm: std::rc::Rc<VM>, species: Species) -> Self {
let uniform_slot = if species == Species::Robot {
Slot::new(id, 6, "Battery".to_string(), Class::Battery)
} else {
Slot::new(id, 6, "Uniform".to_string(), Class::Uniform)
};
HumanPlayer {
id,
prefab: Name::new(""),
name: Name::new(""),
vm,
species,
damage: 0.0,
hydration: 5.0,
nutrition: 50.0,
oxygenation: 0.024,
food_quality: 0.75,
mood: 1.0,
hygiene: 1.0,
left_hand_slot: Slot::new(id, 0, "LeftHand".to_string(), Class::None),
right_hand_slot: Slot::new(id, 1, "RightHand".to_string(), Class::None),
suit_slot: Slot::new(id, 2, "Suit".to_string(), Class::Suit),
helmet_slot: Slot::new(id, 3, "Helmet".to_string(), Class::Helmet),
glasses_slot: Slot::new(id, 4, "Glasses".to_string(), Class::Glasses),
backpack_slot: Slot::new(id, 5, "Back".to_string(), Class::Back),
uniform_slot,
toolbelt_slot: Slot::new(id, 7, "Belt".to_string(), Class::Belt),
}
}
pub fn update_entity_info(&mut self, info: &EntityInfo) {
self.hydration = info.hydration;
self.nutrition = info.nutrition;
self.oxygenation = info.oxygenation;
self.food_quality = info.food_quality;
self.mood = info.mood;
self.hygiene = info.hygiene;
}
pub fn update_slots_from_info(&mut self, info: &BTreeMap<u32, SlotOccupantInfo>) {
for (index, slot_info) in info {
match index {
0 => {
self.left_hand_slot.occupant.replace(slot_info.clone());
}
1 => {
self.right_hand_slot.occupant.replace(slot_info.clone());
}
2 => {
self.helmet_slot.occupant.replace(slot_info.clone());
}
3 => {
self.suit_slot.occupant.replace(slot_info.clone());
}
4 => {
self.backpack_slot.occupant.replace(slot_info.clone());
}
5 => {
self.uniform_slot.occupant.replace(slot_info.clone());
}
6 => {
self.toolbelt_slot.occupant.replace(slot_info.clone());
}
7 => {
self.glasses_slot.occupant.replace(slot_info.clone());
}
_ => {}
}
}
}
}
impl Thermal for HumanPlayer {
fn get_radiation_factor(&self) -> f32 {
0.1
}
fn get_convection_factor(&self) -> f32 {
0.1
}
fn debug_thermal(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"radiation: {}, convection: {}",
self.get_radiation_factor(),
self.get_convection_factor()
)
}
}
impl Storage for HumanPlayer {
fn debug_storage(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "slots: {:?}", self.get_slots())
}
fn get_slots(&self) -> Vec<(usize, &Slot)> {
vec![
(0, &self.left_hand_slot),
(1, &self.right_hand_slot),
(2, &self.helmet_slot),
(3, &self.suit_slot),
(4, &self.backpack_slot),
(5, &self.uniform_slot),
(6, &self.toolbelt_slot),
(7, &self.glasses_slot),
]
}
fn get_slots_mut(&mut self) -> Vec<(usize, &mut Slot)> {
vec![
(0, &mut self.left_hand_slot),
(1, &mut self.right_hand_slot),
(2, &mut self.helmet_slot),
(3, &mut self.suit_slot),
(4, &mut self.backpack_slot),
(5, &mut self.uniform_slot),
(6, &mut self.toolbelt_slot),
(7, &mut self.glasses_slot),
]
}
fn slots_count(&self) -> usize {
8
}
fn get_slot(&self, index: usize) -> Option<&Slot> {
match index {
0 => Some(&self.left_hand_slot),
1 => Some(&self.right_hand_slot),
2 => Some(&self.helmet_slot),
3 => Some(&self.suit_slot),
4 => Some(&self.backpack_slot),
5 => Some(&self.uniform_slot),
6 => Some(&self.toolbelt_slot),
7 => Some(&self.glasses_slot),
_ => None,
}
}
fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot> {
match index {
0 => Some(&mut self.left_hand_slot),
1 => Some(&mut self.right_hand_slot),
2 => Some(&mut self.helmet_slot),
3 => Some(&mut self.suit_slot),
4 => Some(&mut self.backpack_slot),
5 => Some(&mut self.uniform_slot),
6 => Some(&mut self.toolbelt_slot),
7 => Some(&mut self.glasses_slot),
_ => None,
}
}
}
impl Human for HumanPlayer {
fn debug_human(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "species: {:?}, damage: {}, hydration: {}, nutrition:, {}, oxygenation: {}, food_quality: {}, mood: {}, hygiene: {}, artificial: {}, battery: {:?}",
self.get_species(),
self.get_damage(),
self.get_hydration(),
self.get_nutrition(),
self.get_oxygenation(),
self.get_food_quality(),
self.get_mood(),
self.get_hygiene(),
self.is_artificial(),
self.robot_battery()
)
}
fn get_species(&self) -> Species {
self.species
}
fn get_damage(&self) -> f32 {
self.damage
}
fn set_damage(&mut self, damage: f32) {
self.damage = damage;
}
fn get_hydration(&self) -> f32 {
self.hydration
}
fn set_hydration(&mut self, hydration: f32) {
self.hydration = hydration.clamp(0.0, MAX_HYDRATION);
}
fn hydration_state(&self) -> super::traits::StatState {
if self.hydration < CRITICAL_HYDRATION {
return StatState::Critical;
}
if self.hydration < WARNING_HYDRATION {
return StatState::Warning;
}
StatState::Normal
}
fn get_nutrition(&self) -> f32 {
self.nutrition
}
fn set_nutrition(&mut self, nutrition: f32) {
self.nutrition = nutrition.clamp(0.0, MAX_NUTRITION);
}
fn nutrition_state(&self) -> StatState {
if self.nutrition < CRITICAL_NUTRITION {
return StatState::Critical;
}
if self.nutrition < WARNING_NUTRITION {
return StatState::Warning;
}
StatState::Normal
}
fn get_oxygenation(&self) -> f32 {
self.oxygenation
}
fn set_oxygenation(&mut self, oxygenation: f32) {
self.oxygenation = oxygenation.clamp(0.0, MAX_OXYGENATION);
}
fn get_food_quality(&self) -> f32 {
self.food_quality
}
fn set_food_quality(&mut self, quality: f32) {
self.food_quality = quality.clamp(0.0, MAX_FOOD_QUALITY);
}
fn get_mood(&self) -> f32 {
self.mood
}
fn set_mood(&mut self, mood: f32) {
self.mood = mood.clamp(0.0, MAX_MOOD);
}
fn mood_state(&self) -> StatState {
if self.mood < CRITICAL_MOOD {
return StatState::Critical;
}
if self.mood < WARNING_MOOD {
return StatState::Warning;
}
StatState::Normal
}
fn get_hygiene(&self) -> f32 {
self.hygiene
}
fn set_hygiene(&mut self, hygiene: f32) {
self.hygiene = hygiene.clamp(0.0, MAX_HYGIENE);
}
fn hygiene_state(&self) -> StatState {
if self.hygiene < CRITICAL_HYGIENE {
return StatState::Critical;
}
if self.hygiene < WARNING_HYGIENE {
return StatState::Warning;
}
StatState::Normal
}
fn is_artificial(&self) -> bool {
self.species == Species::Robot
}
fn robot_battery(&self) -> Option<super::VMObject> {
if self.species != Species::Robot {
return None;
}
self.uniform_slot()
.occupant
.as_ref()
.and_then(|info| self.vm.get_object(info.id))
}
fn suit_slot(&self) -> &Slot {
&self.suit_slot
}
fn suit_slot_mut(&mut self) -> &mut Slot {
&mut self.suit_slot
}
fn helmet_slot(&self) -> &Slot {
&self.helmet_slot
}
fn helmet_slot_mut(&mut self) -> &mut Slot {
&mut self.helmet_slot
}
fn glasses_slot(&self) -> &Slot {
&self.glasses_slot
}
fn glasses_slot_mut(&mut self) -> &mut Slot {
&mut self.glasses_slot
}
fn backpack_slot(&self) -> &Slot {
&self.backpack_slot
}
fn backpack_slot_mut(&mut self) -> &mut Slot {
&mut self.backpack_slot
}
fn uniform_slot(&self) -> &Slot {
&self.uniform_slot
}
fn uniform_slot_mut(&mut self) -> &mut Slot {
&mut self.uniform_slot
}
fn toolbelt_slot(&self) -> &Slot {
&self.toolbelt_slot
}
fn toolbelt_slot_mut(&mut self) -> &mut Slot {
&mut self.toolbelt_slot
}
fn left_hand_slot(&self) -> &Slot {
&self.left_hand_slot
}
fn left_hand_slot_mut(&mut self) -> &mut Slot {
&mut self.left_hand_slot
}
fn right_hand_slot(&self) -> &Slot {
&self.right_hand_slot
}
fn right_hand_slot_mut(&mut self) -> &mut Slot {
&mut self.right_hand_slot
}
}

View File

@@ -0,0 +1,594 @@
macro_rules! object_trait {
(@intf {$trt:ident}) => {
paste::paste! {
#[allow(missing_docs, unused)]
pub type [<$trt Ref>]<'a> = &'a dyn $trt;
#[allow(missing_docs, unused)]
pub type [<$trt RefMut>]<'a> = &'a mut dyn $trt;
}
};
(@body $trait_name:ident { $($trt:ident),* }; ) => {
fn get_id(&self) -> &crate::vm::object::ObjectID;
fn set_id(&mut self, id: crate::vm::object::ObjectID);
fn get_prefab(&self) -> &crate::vm::object::Name;
fn get_mut_prefab(&mut self) -> &mut crate::vm::object::Name;
fn get_name(&self) -> &crate::vm::object::Name;
fn get_mut_name(&mut self) -> &mut crate::vm::object::Name;
fn get_vm(&self) -> &std::rc::Rc<crate::vm::VM>;
fn set_vm(&mut self, vm: std::rc::Rc<crate::vm::VM>);
fn type_name(&self) -> &str;
fn as_object(&self) -> &dyn $trait_name;
fn as_mut_object(&mut self) -> &mut dyn $trait_name;
paste::paste! {
$(
#[doc = "Return a `& dyn " $trt "` if implemented by the object"]
#[inline(always)]
fn [<as_ $trt:snake>](&self) -> Option<[<$trt Ref>]> {
None
}
#[doc = "Return a `&mut dyn " $trt "` if implemented by the object"]
#[inline(always)]
fn [<as_mut_ $trt:snake>](&mut self) -> Option<[<$trt RefMut>]> {
None
}
)*
}
};
(@intf_struct $trait_name:ident { $($trt:ident),* };) => {
paste::paste! {
pub struct [<$trait_name Interfaces>]<'a> {
$(
pub [<$trt:snake>]: Option<[<$trt Ref>]<'a>>,
)*
}
impl<'a> [<$trait_name Interfaces>]<'a> {
pub fn [<from_ $trait_name:snake>](obj: &'a dyn $trait_name) -> [<$trait_name Interfaces>]<'a> {
[<$trait_name Interfaces>] {
$(
[<$trt:snake>]: obj.[<as_ $trt:snake>](),
)*
}
}
}
impl<'a> Debug for [<$trait_name Interfaces>]<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
$(
write!(f, "{}: {:?}, ", stringify!([<$trt:snake>]), &self.[<$trt:snake>])?;
)*
write!(f, "")
}
}
pub enum [<$trait_name Ref>]<'a> {
DynRef(&'a dyn $trait_name),
VMObject(crate::vm::object::VMObject),
}
impl<'a> [<$trait_name Ref>]<'a> {
pub fn from_ref(reference: &'a dyn $trait_name) -> Self {
Self::DynRef(reference)
}
pub fn from_vm_object(obj: crate::vm::object::VMObject) -> Self {
Self::VMObject(obj)
}
pub fn get_id(&self) -> u32 {
match self {
Self::DynRef(reference) => *reference.get_id(),
Self::VMObject(obj) => *obj.borrow().get_id(),
}
}
/// call func on the dyn reference or a borrow of the vm object
pub fn map<F, R>(&self, mut func: F ) -> R
where
F: std::ops::FnMut(& dyn $trait_name) -> R
{
match self {
Self::DynRef(reference) => func(*reference),
Self::VMObject(obj) => {
let obj_ref = obj.borrow();
func(&*obj_ref)
}
}
}
}
pub enum [<$trait_name RefMut>]<'a> {
DynRef(&'a mut dyn $trait_name),
VMObject(crate::vm::object::VMObject),
}
impl<'a> [<$trait_name RefMut>]<'a> {
pub fn from_ref(reference: &'a mut dyn $trait_name) -> Self {
Self::DynRef(reference)
}
pub fn from_vm_object(obj: crate::vm::object::VMObject) -> Self {
Self::VMObject(obj)
}
pub fn get_id(&self) -> u32 {
match self {
Self::DynRef(reference) => *reference.get_id(),
Self::VMObject(obj) => *obj.borrow().get_id(),
}
}
/// call func on the dyn reference or a borrow of the vm object
pub fn map<F, R>(&mut self, mut func: F ) -> R
where
F: std::ops::FnMut(&mut dyn $trait_name) -> R
{
match self {
Self::DynRef(reference) => func(*reference),
Self::VMObject(obj) => {
let mut obj_ref = obj.borrow_mut();
func(&mut *obj_ref)
}
}
}
}
}
};
( $trait_name:ident $(: $($bound:tt)* )? {$($trt:ident),*}) => {
$(
$crate::vm::object::macros::object_trait!{@intf {$trt}}
)*
#[doc = concat!("Generated with: ", stringify!($($trt),*))]
pub trait $trait_name $(: $($bound)* )? {
$crate::vm::object::macros::object_trait!{@body $trait_name {$($trt),*}; }
}
$crate::vm::object::macros::object_trait!{@intf_struct $trait_name {$($trt),*}; }
};
}
pub(crate) use object_trait;
/// use macro_rules_attribute::derive to apply this macro to a struct
///
/// use `#[custom(object_id)]`, `#[custom(object_prefab)]`, `#[custom(object_name)]`, and `#[custom(object_vm_ref)]`
/// to tag struct fields appropriately
///
/// the tags for `id`, `prefab`, and `name` may appear in any order but `vm_ref` must come last
///
/// - `id` must be `crate::vm::object::ObjectID`
/// - `prefab` and `name` must be `crate::vm::object::Name`
/// - `vm_ref` must be `std::rc::Rc<crate::vm::VM>`
macro_rules! ObjectInterface {
{
@trt_cond_impl $trt:path => $trt_cond:path
} => {
paste::paste!{
#[inline(always)]
fn [<as_ $trt:snake>](&self) -> Option<[<$trt Ref>]> {
if $trt_cond(self) {
Some(self)
} else {
None
}
}
#[inline(always)]
fn [<as_mut_ $trt:snake>](&mut self) -> Option<[<$trt RefMut>]> {
if $trt_cond(self) {
Some(self)
} else {
None
}
}
}
};
{
@trt_cond_impl $trt:path
} => {
paste::paste!{
#[inline(always)]
fn [<as_ $trt:snake>](&self) -> Option<[<$trt Ref>]> {
Some(self)
}
#[inline(always)]
fn [<as_mut_ $trt:snake>](&mut self) -> Option<[<$trt RefMut>]> {
Some(self)
}
}
};
{
@body_final
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@id $id_field:ident: $id_typ:ty;
@prefab $prefab_field:ident: $prefab_typ:ty;
@name $name_field:ident: $name_typ:ty;
@vm_ref $vm_ref_field:ident: $vm_ref_typ:ty;
} => {
impl $trait_name for $struct {
fn get_id(&self) -> &crate::vm::object::ObjectID {
&self.$id_field
}
fn set_id(&mut self, id: crate::vm::object::ObjectID) {
self.$id_field = id;
}
fn get_prefab(&self) -> &$prefab_typ {
&self.$prefab_field
}
fn get_mut_prefab(&mut self) -> &mut $prefab_typ {
&mut self.$prefab_field
}
fn get_name(&self) -> &$name_typ {
&self.$name_field
}
fn get_mut_name(&mut self) -> &mut $name_typ {
&mut self.$name_field
}
fn get_vm(&self) -> &std::rc::Rc<crate::vm::VM> {
&self.$vm_ref_field
}
fn set_vm(&mut self, vm: std::rc::Rc<crate::vm::VM>) {
self.$vm_ref_field = vm;
}
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
#[inline(always)]
fn as_object(&self) -> &dyn $trait_name {
self
}
#[inline(always)]
fn as_mut_object(&mut self) -> &mut dyn $trait_name {
self
}
$(
$crate::vm::object::macros::ObjectInterface!{@trt_cond_impl $trt $( => $trt_cond)? }
)*
}
};
{
@body_final
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@id $id_field:ident: $id_typ:ty;
@name $name_field:ident: $name_typ:ty;
@prefab $prefab_field:ident: $prefab_typ:ty;
@vm_ref $vm_ref_field:ident: $vm_ref_typ:ty;
} => {
$crate::vm::object::macros::ObjectInterface!{
@body_final
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@id $id_field: $id_typ;
@prefab $prefab_field: $prefab_typ;
@name $name_field: $name_typ;
@vm_ref $vm_ref_field: $vm_ref_typ;
}
};
{
@body_final
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@prefab $prefab_field:ident: $prefab_typ:ty;
@name $name_field:ident: $name_typ:ty;
@id $id_field:ident: $id_typ:ty;
@vm_ref $vm_ref_field:ident: $vm_ref_typ:ty;
} => {
$crate::vm::object::macros::ObjectInterface!{
@body_final
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@id $id_field: $id_typ;
@prefab $prefab_field: $prefab_typ;
@name $name_field: $name_typ;
@vm_ref $vm_ref_field: $vm_ref_typ;
}
};
{
@body_final
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@prefab $prefab_field:ident: $prefab_typ:ty;
@id $id_field:ident: $id_typ:ty;
@name $name_field:ident: $name_typ:ty;
@vm_ref $vm_ref_field:ident: $vm_ref_typ:ty;
} => {
$crate::vm::object::macros::ObjectInterface!{
@body_final
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@id $id_field: $id_typ;
@prefab $prefab_field: $prefab_typ;
@name $name_field: $name_typ;
@vm_ref $vm_ref_field: $vm_ref_typ;
}
};
{
@body_final
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@name $name_field:ident: $name_typ:ty;
@prefab $prefab_field:ident: $prefab_typ:ty;
@id $id_field:ident: $id_typ:ty;
@vm_ref $vm_ref_field:ident: $vm_ref_typ:ty;
} => {
$crate::vm::object::macros::ObjectInterface!{
@body_final
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@id $id_field: $id_typ;
@prefab $prefab_field: $prefab_typ;
@name $name_field: $name_typ;
@vm_ref $vm_ref_field: $vm_ref_typ;
}
};
{
@body_final
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@name $name_field:ident: $name_typ:ty;
@id $id_field:ident: $id_typ:ty;
@prefab $prefab_field:ident: $prefab_typ:ty;
@vm_ref $vm_ref_field:ident: $vm_ref_typ:ty;
} => {
$crate::vm::object::macros::ObjectInterface!{
@body_final
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@id $id_field: $id_typ;
@prefab $prefab_field: $prefab_typ;
@name $name_field: $name_typ;
@vm_ref $vm_ref_field: $vm_ref_typ;
}
};{
@body
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@tags {
$(@$tag:tt $tag_field:ident: $tag_typ:ty;)*
};
#[custom(object_vm_ref)]
$(#[$vm_ref_attr:meta])*
$vm_ref_viz:vis $vm_ref_field:ident: $vm_ref_typ:ty,
$( $rest:tt )*
} => {
$crate::vm::object::macros::ObjectInterface!{
@body
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@tags {$(@$tag $tag_field: $tag_typ;)* @vm_ref $vm_ref_field: $vm_ref_typ;};
$( $rest )*
}
};
{
@body
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@tags {
$(@$tag:tt $tag_field:ident: $tag_typ:ty;)*
};
#[custom(object_name)]
$(#[$name_attr:meta])*
$name_viz:vis $name_field:ident: $name_typ:ty,
$( $rest:tt )*
} => {
$crate::vm::object::macros::ObjectInterface!{
@body
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@tags {$(@$tag $tag_field: $tag_typ;)* @name $name_field: $name_typ;};
$( $rest )*
}
};
{
@body
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@tags {
$(@$tag:tt $tag_field:ident: $tag_typ:ty;)*
};
#[custom(object_prefab)]
$(#[$prefab_attr:meta])*
$prefab_viz:vis $prefab_field:ident: $prefab_typ:ty,
$( $rest:tt )*
} => {
$crate::vm::object::macros::ObjectInterface!{
@body
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@tags {$(@$tag $tag_field: $tag_typ;)* @prefab $prefab_field: $prefab_typ;};
$( $rest )*
}
};
{
@body
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@tags {
$(@$tag:tt $tag_field:ident: $tag_typ:ty;)*
};
#[custom(object_id)]
$(#[$id_attr:meta])*
$id_viz:vis $id_field:ident: $id_typ:ty,
$( $rest:tt )*
} => {
$crate::vm::object::macros::ObjectInterface!{
@body
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@tags {$(@$tag $tag_field: $tag_typ;)* @id $id_field: $id_typ;};
$( $rest )*
}
};
{
@body
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@tags {
$(@$tag:tt $tag_field:ident: $tag_typ:ty;)*
};
$(#[$field:meta])*
$field_viz:vis
$field_name:ident : $field_ty:ty,
$( $rest:tt )*
} => {
$crate::vm::object::macros::ObjectInterface!{
@body
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@tags {$(@$tag $tag_field: $tag_typ;)*};
$( $rest )*
}
};
{
@body
@trt $trait_name:ident; $struct:ident;
@impls $($trt:path $([ $trt_cond:path ])?),*;
@tags {
$(@$tag:tt $tag_field:ident: $tag_typ:ty;)*
};
} => {
$crate::vm::object::macros::ObjectInterface!{
@body_final
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
$(
@$tag $tag_field: $tag_typ;
)*
}
};
{
#[custom(implements($trait_name:ident {$($trt:path $([$trt_cond:path])?),*}))]
$( #[$attr:meta] )*
$viz:vis struct $struct:ident {
$( $body:tt )*
}
} => {
$crate::vm::object::macros::ObjectInterface!{
@body
@trt $trait_name; $struct;
@impls $($trt $([ $trt_cond ])?),*;
@tags {};
$( $body )*
}
};
}
pub(crate) use ObjectInterface;
#[allow(unused_macros)]
macro_rules! ObjectTrait {
{
#[custom(object_trait = $trait_name:ident)]
$(#[$attr:meta])*
$viz:vis trait $trt:ident $(: $($bound:tt)* )? {
$($tbody:tt)*
}
} => {
$(#[$attr])*
$viz trait $trt: $($($bound)* +)? $trait_name {
$($tbody)*
}
};
}
#[allow(unused_imports)]
pub(crate) use ObjectTrait;
macro_rules! tag_object_traits {
{
@tag
tag=$trt_name:ident $(: $($obj_bound:tt)* )?;
acc={ $($tagged_trt:ident,)* };
$(#[$attr:meta])*
$viz:vis trait $trt:ident $(: $trt_bound_first:tt $(+ $trt_bound_others:tt)* )? {
$($tbody:tt)*
}
$($used:tt)*
} => {
#[doc = concat!("Autotagged with ", stringify!($trt_name))]
$(#[$attr])*
$viz trait $trt : $( $trt_bound_first $(+ $trt_bound_others)* +)? $trt_name {
$($tbody)*
paste::paste! {
fn [<debug_ $trt:snake>](&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
}
}
impl<'a> Debug for dyn $trt + 'a {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}{{", stringify!($trt))?;
paste::paste! {
self.[<debug_ $trt:snake>](f)?;
}
write!(f, "}}")
}
}
$crate::vm::object::macros::tag_object_traits!{
@tag
tag=$trt_name $(: $($obj_bound)* )?;
acc={ $($tagged_trt,)* $trt, };
$($used)*
}
};
{
@tag
tag=$trt_name:ident $(: $($obj_bound:tt)* )?;
acc={ $($tagged_trt:ident,)* };
impl $name:ident for $trt:path {
$($body:tt)*
}
$($used:tt)*
} => {
/// Untouched by tag macro
impl $name for $trt {
$($body)*
}
$crate::vm::object::macros::tag_object_traits!{
@tag
tag=$trt_name $(: $($obj_bound)* )?;
acc={ $($tagged_trt,)* };
$($used)*
}
};
{
@tag
tag=$trt_name:ident $(: $($obj_bound:tt)* )?;
acc={ $($tagged_trt:ident,)* };
} => {
// end tagged traits {$trt_name}
$crate::vm::object::macros::object_trait!($trt_name $(: $($obj_bound)* )? { $($tagged_trt),* });
};
{ #![object_trait($trt_name:ident $(: $($bound:tt)* )? )] $($tree:tt)* } => {
$crate::vm::object::macros::tag_object_traits!{ @tag tag=$trt_name; acc={}; $($tree)* }
};
}
pub(crate) use tag_object_traits;

View File

@@ -0,0 +1,152 @@
use std::rc::Rc;
use stationeers_data::{enums::prefabs::StationpediaPrefab, templates::ObjectTemplate};
use crate::{
errors::TemplateError,
vm::object::{
templates::{ObjectInfo, Prefab},
Name, VMObject,
},
};
use crate::{
interpreter::Program,
vm::{object::LogicField, VM},
};
use strum::EnumProperty;
use super::ObjectID;
pub mod structs;
#[allow(unused)]
pub fn object_from_frozen(
obj: &ObjectInfo,
id: ObjectID,
vm: &Rc<VM>,
) -> Result<Option<VMObject>, TemplateError> {
#[allow(clippy::cast_possible_wrap)]
let Some(hash) = obj
.prefab
.as_ref()
.map(|name| const_crc32::crc32(name.as_bytes()) as i32)
else {
return Ok(None);
};
let prefab = StationpediaPrefab::from_repr(hash);
#[allow(clippy::match_single_binding)]
match prefab {
Some(prefab @ StationpediaPrefab::ItemIntegratedCircuit10) => {
let template = vm
.get_template(Prefab::Hash(hash))
.ok_or(TemplateError::NoTemplateForPrefab(Prefab::Hash(hash)))?;
let ObjectTemplate::ItemLogicMemory(template) = template else {
return Err(TemplateError::IncorrectTemplate(
"ItemIntegratedCircuit10".to_string(),
Prefab::Name("ItemIntegratedCircuit10".to_string()),
template,
));
};
Ok(Some(VMObject::new(structs::ItemIntegratedCircuit10 {
id,
vm: vm.clone(),
name: Name::new(
&(obj
.name
.clone()
.unwrap_or_else(|| prefab.get_str("name").unwrap().to_string())),
),
prefab: Name::from_prefab_name(prefab.as_ref()),
fields: template
.logic
.logic_types
.iter()
.map(|(key, access)| {
(
*key,
LogicField {
field_type: *access,
value: obj
.logic_values
.as_ref()
.and_then(|values| values.get(key))
.copied()
.unwrap_or(0.0),
},
)
})
.collect(),
memory: obj
.memory
.clone()
.map(TryInto::try_into)
.transpose()
.map_err(|vec: Vec<f64>| TemplateError::MemorySize(vec.len(), 512))?
.unwrap_or([0.0f64; 512]),
parent_slot: None,
registers: obj
.circuit
.as_ref()
.map(|circuit| circuit.registers.clone().try_into())
.transpose()
.map_err(|vec: Vec<f64>| TemplateError::MemorySize(vec.len(), 18))?
.unwrap_or([0.0f64; 18]),
ip: obj
.circuit
.as_ref()
.map(|circuit| circuit.instruction_pointer as usize)
.unwrap_or(0),
next_ip: 0,
ic: obj
.circuit
.as_ref()
.map(|circuit| circuit.yield_instruction_count)
.unwrap_or(0),
aliases: obj
.circuit
.as_ref()
.map(|circuit| circuit.aliases.clone())
.unwrap_or_default(),
defines: obj
.circuit
.as_ref()
.map(|circuit| circuit.defines.clone())
.unwrap_or_default(),
pins: obj
.device_pins
.as_ref()
.map(|pins| {
(0..6)
.map(|index| pins.get(&index).copied())
.collect::<Vec<_>>()
.try_into()
.unwrap() // fixed sized iterator into array should not panic
})
.unwrap_or_default(),
state: obj
.circuit
.as_ref()
.map(|circuit| circuit.state.clone())
.unwrap_or(crate::interpreter::ICState::Start),
code: obj.source_code.clone().unwrap_or_default(),
damage: obj.damage.unwrap_or(0.0),
program: obj
.source_code
.as_ref()
.map(|code| {
if code.is_empty() {
Program::default()
} else {
Program::from_code_with_invalid(code)
}
})
.unwrap_or_default(),
})))
}
// Some(StationpediaPrefab::StructureCircuitHousing) => Some()
// Some(StationpediaPrefab::StructureRocketCircuitHousing) => Some()
_ => Ok(None),
}
}

View File

@@ -0,0 +1,4 @@
mod circuit_holder;
mod integrated_circuit;
pub use integrated_circuit::ItemIntegratedCircuit10;

View File

@@ -0,0 +1,450 @@
use crate::{
network::{CableConnectionType, Connection},
vm::{
object::{
errors::LogicError, macros::ObjectInterface, traits::*, Name, ObjectID, Slot, VMObject,
},
VM,
},
};
use macro_rules_attribute::derive;
use stationeers_data::enums::{
basic::Class,
prefabs::StationpediaPrefab,
script::{LogicSlotType, LogicType},
ConnectionRole,
};
use std::{collections::BTreeMap, rc::Rc};
use strum::EnumProperty;
#[derive(ObjectInterface!)]
#[custom(implements(Object { Structure, Device, Storage, Logicable, CircuitHolder }))]
pub struct StructureCircuitHousing {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub error: i32,
pub on: bool,
pub setting: f64,
pub slot: Slot,
pub pins: [Option<ObjectID>; 6],
pub connections: [crate::network::Connection; 2],
}
#[allow(dead_code)]
impl StructureCircuitHousing {
pub fn new(id: ObjectID, vm: Rc<VM>) -> Self {
StructureCircuitHousing {
id,
prefab: Name {
value: StationpediaPrefab::StructureCircuitHousing.to_string(),
hash: StationpediaPrefab::StructureCircuitHousing as i32,
},
name: Name::new(
StationpediaPrefab::StructureCircuitHousing
.get_str("name")
.unwrap(),
),
vm,
error: 0,
on: true,
setting: 0.0,
slot: Slot {
parent: id,
index: 0,
name: "Programmable Chip".to_string(),
class: Class::ProgrammableChip,
readable_logic: vec![
LogicSlotType::Class,
LogicSlotType::Damage,
LogicSlotType::LineNumber,
LogicSlotType::MaxQuantity,
LogicSlotType::OccupantHash,
LogicSlotType::Occupied,
LogicSlotType::PrefabHash,
LogicSlotType::Quantity,
LogicSlotType::ReferenceId,
LogicSlotType::SortingClass,
],
writeable_logic: vec![],
occupant: None,
proxy: false,
},
pins: [None, None, None, None, None, None],
connections: [
Connection::CableNetwork {
net: None,
typ: CableConnectionType::Data,
role: ConnectionRole::Input,
},
Connection::CableNetwork {
net: None,
typ: CableConnectionType::Power,
role: ConnectionRole::None,
},
],
}
}
}
impl Structure for StructureCircuitHousing {
fn is_small_grid(&self) -> bool {
true
}
fn debug_structure(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "small_grid: {}", self.is_small_grid())
}
}
impl Storage for StructureCircuitHousing {
fn debug_storage(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "slots: {:?}", self.get_slots())
}
fn slots_count(&self) -> usize {
1
}
fn get_slot(&self, index: usize) -> Option<&Slot> {
if index != 0 {
None
} else {
Some(&self.slot)
}
}
fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot> {
if index != 0 {
None
} else {
Some(&mut self.slot)
}
}
fn get_slots(&self) -> Vec<(usize, &Slot)> {
vec![(0, &self.slot)]
}
fn get_slots_mut(&mut self) -> Vec<(usize, &mut Slot)> {
vec![(0, &mut self.slot)]
}
}
impl Logicable for StructureCircuitHousing {
fn debug_logicable(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let values: BTreeMap<LogicType, Option<f64>> = self
.valid_logic_types()
.into_iter()
.map(|lt| (lt, self.get_logic(lt).ok()))
.collect();
write!(
f,
"prefab_hash: {}, name_hash: {}, readable: {}, writable: {}, values{:?}",
self.prefab_hash(),
self.name_hash(),
self.is_logic_readable(),
self.is_logic_writeable(),
values
)
}
fn prefab_hash(&self) -> i32 {
self.get_prefab().hash
}
fn name_hash(&self) -> i32 {
self.get_name().hash
}
fn is_logic_readable(&self) -> bool {
true
}
fn is_logic_writeable(&self) -> bool {
true
}
fn can_logic_read(&self, lt: LogicType) -> bool {
use LogicType::*;
matches!(
lt,
Error
| LineNumber
| NameHash
| On
| Power
| PrefabHash
| ReferenceId
| RequiredPower
| Setting
)
}
fn can_logic_write(&self, lt: LogicType) -> bool {
use LogicType::*;
matches!(lt, LineNumber | On | Setting)
}
fn get_logic(&self, lt: LogicType) -> Result<f64, LogicError> {
match lt {
LogicType::PrefabHash => Ok(self.get_prefab().hash as f64),
LogicType::NameHash => Ok(self.get_name().hash as f64),
LogicType::ReferenceId => Ok(*self.get_id() as f64),
LogicType::Error => Ok(self.error as f64),
LogicType::LineNumber => {
let result = self.slot.occupant.as_ref().and_then(|info| {
self.vm.get_object(info.id).and_then(|obj| {
obj.borrow()
.as_logicable()
.map(|logicable| logicable.get_logic(LogicType::LineNumber))
})
});
result.unwrap_or(Ok(0.0))
}
LogicType::On => Ok(self.on as i32 as f64),
LogicType::Power => {
if let Connection::CableNetwork { net, .. } = self.connections[1] {
if net.is_some() {
Ok(1.0)
} else {
Ok(0.0)
}
} else {
Ok(0.0)
}
}
LogicType::RequiredPower => {
if let Connection::CableNetwork { net, .. } = self.connections[1] {
if net.is_some() {
if self.on {
Ok(10.0)
} else {
Ok(0.0)
}
} else {
Ok(-1.0)
}
} else {
Ok(-1.0)
}
} // 10 if on
LogicType::Setting => Ok(self.setting),
_ => Err(LogicError::CantRead(lt)),
}
}
fn set_logic(&mut self, lt: LogicType, value: f64, force: bool) -> Result<(), LogicError> {
match lt {
LogicType::LineNumber => self
.slot
.occupant
.as_ref()
.and_then(|info| {
self.vm.get_object(info.id).and_then(|obj| {
obj.borrow_mut().as_mut_logicable().map(|logicable| {
logicable.set_logic(LogicType::LineNumber, value, force)
})
})
})
.unwrap_or(Err(LogicError::CantWrite(lt))),
LogicType::On => {
self.on = value != 0.0;
Ok(())
}
LogicType::Setting => {
self.setting = value;
Ok(())
}
_ => Err(LogicError::CantWrite(lt)),
}
}
fn can_slot_logic_read(&self, _slt: LogicSlotType, _indexx: f64) -> bool {
false
}
fn get_slot_logic(&self, _slt: LogicSlotType, index: f64) -> Result<f64, LogicError> {
Err(LogicError::SlotIndexOutOfRange(index, self.slots_count()))
}
fn valid_logic_types(&self) -> Vec<LogicType> {
use LogicType::*;
vec![
Error,
LineNumber,
NameHash,
On,
Power,
PrefabHash,
ReferenceId,
RequiredPower,
Setting,
]
}
fn known_modes(&self) -> Option<Vec<(u32, String)>> {
None
}
}
impl Device for StructureCircuitHousing {
fn debug_device(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"pins: {:?}, connections: {:?}",
self.pins, self.connections
)
}
fn has_reagents(&self) -> bool {
false
}
fn has_atmosphere(&self) -> bool {
false
}
fn has_lock_state(&self) -> bool {
false
}
fn has_mode_state(&self) -> bool {
false
}
fn has_open_state(&self) -> bool {
false
}
fn has_color_state(&self) -> bool {
false
}
fn has_activate_state(&self) -> bool {
false
}
fn has_on_off_state(&self) -> bool {
true
}
fn get_reagents(&self) -> Vec<(u8, f64)> {
vec![]
}
fn set_reagents(&mut self, _reagents: &[(u8, f64)]) {
// nope
}
fn add_reagents(&mut self, _reagents: &[(u8, f64)]) {
// nope
}
fn connection_list(&self) -> &[crate::network::Connection] {
&self.connections
}
fn connection_list_mut(&mut self) -> &mut [crate::network::Connection] {
&mut self.connections
}
fn device_pins(&self) -> Option<&[Option<ObjectID>]> {
Some(&self.pins)
}
fn device_pins_mut(&mut self) -> Option<&mut [Option<ObjectID>]> {
Some(&mut self.pins)
}
fn can_slot_logic_write(&self, _slt: LogicSlotType, _index: f64) -> bool {
false
}
fn set_slot_logic(
&mut self,
slt: LogicSlotType,
index: f64,
_value: f64,
_force: bool,
) -> Result<(), LogicError> {
Err(LogicError::CantSlotWrite(slt, index))
}
}
impl CircuitHolder for StructureCircuitHousing {
fn debug_circuit_holder(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "error: {}, setting: {}", self.error, self.setting)
}
fn clear_error(&mut self) {
self.error = 0
}
fn set_error(&mut self, state: i32) {
self.error = state;
}
/// i32::MAX is db
fn get_logicable_from_index(
&self,
device: i32,
connection: Option<usize>,
) -> Option<ObjectRef> {
if device == i32::MAX {
// self
if let Some(connection) = connection {
self.connections.get(connection).and_then(|conn| {
if let Connection::CableNetwork { net: Some(net), .. } = conn {
self.vm.get_network(*net).map(ObjectRef::from_vm_object)
} else {
None
}
})
} else {
Some(ObjectRef::from_ref(self.as_object()))
}
} else {
if device < 0 {
return None;
}
self.pins.get(device as usize).and_then(|pin| {
pin.and_then(|id| self.vm.get_object(id).map(ObjectRef::from_vm_object))
})
}
}
/// i32::MAX is db
fn get_logicable_from_index_mut(
&mut self,
device: i32,
connection: Option<usize>,
) -> Option<ObjectRefMut> {
if device == i32::MAX {
// self
if let Some(connection) = connection {
self.connections.get(connection).and_then(|conn| {
if let Connection::CableNetwork { net: Some(net), .. } = conn {
self.vm.get_network(*net).map(ObjectRefMut::from_vm_object)
} else {
None
}
})
} else {
Some(ObjectRefMut::from_ref(self.as_mut_object()))
}
} else {
if device < 0 {
return None;
}
self.pins.get(device as usize).and_then(|pin| {
pin.and_then(|id| self.vm.get_object(id).map(ObjectRefMut::from_vm_object))
})
}
}
fn get_logicable_from_id(
&self,
device: ObjectID,
connection: Option<usize>,
) -> Option<ObjectRef> {
if connection.is_some() {
return None; // this functionality is disabled in the game, no network access via ReferenceId
}
if device == self.id {
return Some(ObjectRef::from_ref(self.as_object()));
}
self.vm.get_object(device).map(ObjectRef::from_vm_object)
}
fn get_logicable_from_id_mut(
&mut self,
device: ObjectID,
connection: Option<usize>,
) -> Option<ObjectRefMut> {
if connection.is_some() {
return None; // this functionality is disabled in the game, no network access via ReferenceId
}
if device == self.id {
return Some(ObjectRefMut::from_ref(self.as_mut_object()));
}
self.vm.get_object(device).map(ObjectRefMut::from_vm_object)
}
fn get_ic(&self) -> Option<VMObject> {
self.slot
.occupant
.as_ref()
.and_then(|info| self.vm.get_object(info.id))
}
fn halt_and_catch_fire(&mut self) {
// TODO: do something here??
}
}

View File

@@ -0,0 +1,492 @@
use crate::{
errors::{ICError, LineError},
interpreter::{instructions::IC10Marker, ICState, Program},
vm::{
instructions::{operands::Operand, Instruction},
object::{
errors::{LogicError, MemoryError},
macros::ObjectInterface,
traits::*,
LogicField, MemoryAccess, Name, ObjectID, Slot, VMObject,
},
VM,
},
};
use macro_rules_attribute::derive;
use stationeers_data::enums::{
basic::{Class, GasType, SortingClass},
script::{LogicSlotType, LogicType},
};
use std::{collections::BTreeMap, rc::Rc};
static RETURN_ADDRESS_INDEX: usize = 17;
static STACK_POINTER_INDEX: usize = 16;
#[derive(ObjectInterface!, Debug)]
#[custom(implements(Object {
Item, Storage, Logicable,
MemoryReadable, MemoryWritable,
SourceCode, IntegratedCircuit,
Programmable
}))]
pub struct ItemIntegratedCircuit10 {
#[custom(object_id)]
pub id: ObjectID,
#[custom(object_prefab)]
pub prefab: Name,
#[custom(object_name)]
pub name: Name,
#[custom(object_vm_ref)]
pub vm: Rc<VM>,
pub fields: BTreeMap<LogicType, LogicField>,
pub memory: [f64; 512],
pub parent_slot: Option<ParentSlotInfo>,
pub registers: [f64; 18],
/// Instruction Pointer
pub ip: usize,
pub next_ip: usize,
/// Instruction Count since last yield
pub ic: u16,
pub aliases: BTreeMap<String, Operand>,
pub defines: BTreeMap<String, f64>,
pub pins: [Option<u32>; 6],
pub state: ICState,
pub code: String,
pub program: Program,
pub damage: f32,
}
impl Item for ItemIntegratedCircuit10 {
fn debug_item(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn consumable(&self) -> bool {
false
}
fn filter_type(&self) -> Option<GasType> {
None
}
fn ingredient(&self) -> bool {
false
}
fn max_quantity(&self) -> u32 {
1
}
fn reagents(&self) -> Option<&BTreeMap<String, f64>> {
None
}
fn slot_class(&self) -> Class {
Class::ProgrammableChip
}
fn sorting_class(&self) -> SortingClass {
SortingClass::Default
}
fn get_parent_slot(&self) -> Option<ParentSlotInfo> {
self.parent_slot
}
fn set_parent_slot(&mut self, info: Option<ParentSlotInfo>) {
self.parent_slot = info;
}
fn get_damage(&self) -> f32 {
self.damage
}
fn set_damage(&mut self, damage: f32) {
self.damage = damage.clamp(0.0, 1.0);
}
}
impl Storage for ItemIntegratedCircuit10 {
fn debug_storage(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn slots_count(&self) -> usize {
0
}
fn get_slot(&self, _index: usize) -> Option<&Slot> {
None
}
fn get_slot_mut(&mut self, _index: usize) -> Option<&mut Slot> {
None
}
fn get_slots(&self) -> Vec<(usize, &Slot)> {
vec![]
}
fn get_slots_mut(&mut self) -> Vec<(usize, &mut Slot)> {
vec![]
}
}
impl Logicable for ItemIntegratedCircuit10 {
fn debug_logicable(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn prefab_hash(&self) -> i32 {
self.get_prefab().hash
}
fn name_hash(&self) -> i32 {
self.get_name().hash
}
fn is_logic_readable(&self) -> bool {
true
}
fn is_logic_writeable(&self) -> bool {
true
}
fn can_logic_read(&self, lt: LogicType) -> bool {
self.fields
.get(&lt)
.map(|field| {
matches!(
field.field_type,
MemoryAccess::Read | MemoryAccess::ReadWrite
)
})
.unwrap_or(false)
}
fn can_logic_write(&self, lt: LogicType) -> bool {
self.fields
.get(&lt)
.map(|field| {
matches!(
field.field_type,
MemoryAccess::Write | MemoryAccess::ReadWrite
)
})
.unwrap_or(false)
}
fn get_logic(&self, lt: LogicType) -> Result<f64, LogicError> {
self.fields
.get(&lt)
.and_then(|field| match field.field_type {
MemoryAccess::Read | MemoryAccess::ReadWrite => Some(field.value),
_ => None,
})
.ok_or(LogicError::CantRead(lt))
}
fn set_logic(&mut self, lt: LogicType, value: f64, force: bool) -> Result<(), LogicError> {
self.fields
.get_mut(&lt)
.ok_or(LogicError::CantWrite(lt))
.and_then(|field| match field.field_type {
MemoryAccess::Write | MemoryAccess::ReadWrite => {
field.value = value;
Ok(())
}
_ if force => {
field.value = value;
Ok(())
}
_ => Err(LogicError::CantWrite(lt)),
})
}
fn can_slot_logic_read(&self, _slt: LogicSlotType, _index: f64) -> bool {
false
}
fn get_slot_logic(&self, _slt: LogicSlotType, index: f64) -> Result<f64, LogicError> {
Err(LogicError::SlotIndexOutOfRange(index, self.slots_count()))
}
fn valid_logic_types(&self) -> Vec<LogicType> {
self.fields.keys().copied().collect()
}
fn known_modes(&self) -> Option<Vec<(u32, String)>> {
None
}
}
impl MemoryReadable for ItemIntegratedCircuit10 {
fn debug_memory_readable(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn memory_size(&self) -> usize {
self.memory.len()
}
fn get_memory(&self, index: i32) -> Result<f64, MemoryError> {
if index < 0 {
Err(MemoryError::StackUnderflow(index, self.memory.len()))
} else if index as usize >= self.memory.len() {
Err(MemoryError::StackOverflow(index, self.memory.len()))
} else {
Ok(self.memory[index as usize])
}
}
fn get_memory_slice(&self) -> &[f64] {
&self.memory
}
}
impl MemoryWritable for ItemIntegratedCircuit10 {
fn debug_memory_writable(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn set_memory(&mut self, index: i32, val: f64) -> Result<(), MemoryError> {
if index < 0 {
Err(MemoryError::StackUnderflow(index, self.memory.len()))
} else if index as usize >= self.memory.len() {
Err(MemoryError::StackOverflow(index, self.memory.len()))
} else {
self.memory[index as usize] = val;
Ok(())
}
}
fn clear_memory(&mut self) {
self.memory.fill(0.0);
}
}
impl SourceCode for ItemIntegratedCircuit10 {
fn debug_source_code(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn set_source_code(&mut self, code: &str) -> Result<(), ICError> {
self.program = Program::try_from_code(code)?;
self.code = code.to_string();
Ok(())
}
fn set_source_code_with_invalid(&mut self, code: &str) {
self.program = Program::from_code_with_invalid(code);
self.code = code.to_string();
}
fn get_source_code(&self) -> String {
self.code.clone()
}
fn get_line(&self, line: usize) -> Result<Instruction, ICError> {
self.program.get_line(line)
}
fn get_compile_errors(&self) -> Vec<ICError> {
self.program.errors.clone()
}
}
impl IntegratedCircuit for ItemIntegratedCircuit10 {
fn debug_integrated_circuit(&self,f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
fn get_circuit_holder(&self) -> Option<VMObject> {
self.get_parent_slot()
.and_then(|parent_slot| self.get_vm().get_object(parent_slot.parent))
}
fn get_instruction_pointer(&self) -> u32 {
self.ip as u32
}
fn set_next_instruction(&mut self, next_instruction: f64) {
self.next_ip = next_instruction as usize;
}
fn set_next_instruction_relative(&mut self, offset: f64) {
self.next_ip = (self.ip as f64 + offset) as usize
}
fn reset(&mut self) {
self.ip = 0;
self.ic = 0;
self.registers.fill(0.0);
self.memory.fill(0.0);
self.aliases.clear();
self.defines.clear();
self.state = ICState::Start;
}
fn get_real_target(&self, indirection: u32, target: u32) -> Result<f64, ICError> {
let mut i = indirection;
let mut t = target as f64;
while i > 0 {
if let Some(new_t) = self.registers.get(t as usize) {
t = *new_t;
} else {
return Err(ICError::RegisterIndexOutOfRange(t));
}
i -= 1;
}
Ok(t)
}
fn get_register(&self, indirection: u32, target: u32) -> Result<f64, ICError> {
let t = self.get_real_target(indirection, target)?;
self.registers
.get(t as usize)
.ok_or(ICError::RegisterIndexOutOfRange(t))
.copied()
}
fn set_register(&mut self, indirection: u32, target: u32, val: f64) -> Result<f64, ICError> {
let t = self.get_real_target(indirection, target)?;
let old_val = self
.registers
.get(t as usize)
.ok_or(ICError::RegisterIndexOutOfRange(t))
.copied()?;
self.registers[t as usize] = val;
Ok(old_val)
}
fn get_registers(&self) -> &[f64] {
&self.registers
}
fn get_registers_mut(&mut self) -> &mut [f64] {
&mut self.registers
}
fn set_return_address(&mut self, addr: f64) {
self.registers[RETURN_ADDRESS_INDEX] = addr;
}
fn push_stack(&mut self, val: f64) -> Result<f64, ICError> {
let sp = (self.registers[STACK_POINTER_INDEX].round()) as i32;
if sp < 0 {
Err(MemoryError::StackUnderflow(sp, self.memory.len()).into())
} else if sp as usize >= self.memory.len() {
Err(MemoryError::StackOverflow(sp, self.memory.len()).into())
} else {
let last = self.memory[sp as usize];
self.memory[sp as usize] = val;
self.registers[STACK_POINTER_INDEX] += 1.0;
Ok(last)
}
}
fn pop_stack(&mut self) -> Result<f64, ICError> {
self.registers[STACK_POINTER_INDEX] -= 1.0;
let sp = (self.registers[STACK_POINTER_INDEX].round()) as i32;
if sp < 0 {
Err(MemoryError::StackUnderflow(sp, self.memory.len()).into())
} else if sp as usize >= self.memory.len() {
Err(MemoryError::StackOverflow(sp, self.memory.len()).into())
} else {
let last = self.memory[sp as usize];
Ok(last)
}
}
fn peek_stack(&self) -> Result<f64, ICError> {
let sp = (self.registers[STACK_POINTER_INDEX] - 1.0).round() as i32;
if sp < 0 {
Err(MemoryError::StackUnderflow(sp, self.memory.len()).into())
} else if sp as usize >= self.memory.len() {
Err(MemoryError::StackOverflow(sp, self.memory.len()).into())
} else {
let last = self.memory[sp as usize];
Ok(last)
}
}
fn get_stack(&self, addr: f64) -> Result<f64, ICError> {
let sp = (addr) as i32;
if !(0..(self.memory.len() as i32)).contains(&sp) {
Err(ICError::StackIndexOutOfRange(addr))
} else {
let val = self.memory[sp as usize];
Ok(val)
}
}
fn put_stack(&mut self, addr: f64, val: f64) -> Result<f64, ICError> {
let sp = addr.round() as i32;
if !(0..(self.memory.len() as i32)).contains(&sp) {
Err(ICError::StackIndexOutOfRange(addr))
} else {
let last = self.memory[sp as usize];
self.memory[sp as usize] = val;
Ok(last)
}
}
fn get_aliases(&self) -> &BTreeMap<String, Operand> {
&self.aliases
}
fn get_aliases_mut(&mut self) -> &mut BTreeMap<String, Operand> {
&mut self.aliases
}
fn get_defines(&self) -> &BTreeMap<String, f64> {
&self.defines
}
fn get_defines_mut(&mut self) -> &mut BTreeMap<String, f64> {
&mut self.defines
}
fn get_labels(&self) -> &BTreeMap<String, u32> {
&self.program.labels
}
fn get_state(&self) -> crate::interpreter::ICState {
self.state.clone()
}
fn set_state(&mut self, state: crate::interpreter::ICState) {
self.state = state;
if matches!(self.state, ICState::Yield | ICState::Sleep(..)) {
self.ic = 0;
}
}
fn get_instructions_since_yield(&self) -> u16 {
self.ic
}
}
impl IC10Marker for ItemIntegratedCircuit10 {}
impl Programmable for ItemIntegratedCircuit10 {
fn debug_programmable(&self,f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "UNIMPLEMENTED") //TODO: Implement
}
#[tracing::instrument]
fn step(&mut self, advance_ip_on_err: bool) -> Result<(), crate::errors::ICError> {
tracing::trace!(ignore_error = advance_ip_on_err, "stepping IC");
if matches!(&self.state, ICState::HasCaughtFire) {
tracing::debug!("IC on Fire!");
return Ok(());
}
if matches!(&self.state, ICState::Error(_)) && !advance_ip_on_err {
tracing::debug!("IC in an error state, not advancing");
return Ok(());
}
if let ICState::Sleep(then, sleep_for) = &self.state {
if let Some(duration) = time::Duration::checked_seconds_f64(*sleep_for) {
if let Some(sleep_till) = then.checked_add(duration) {
let now = time::OffsetDateTime::now_local()
.unwrap_or_else(|_| time::OffsetDateTime::now_utc());
if sleep_till > now {
tracing::debug!("Sleeping: {sleep_till} > {now}");
return Ok(());
}
// else sleep duration ended, continue
} else {
return Err(ICError::SleepAdditionError {
duration,
time: *then,
});
}
} else {
return Err(ICError::SleepDurationError(*sleep_for));
}
}
if self.ip >= self.program.len() || self.program.is_empty() {
tracing::debug!("IC at end of program");
self.state = ICState::Ended;
return Ok(());
}
self.next_ip = self.ip + 1;
self.state = ICState::Running;
let line = self.program.get_line(self.ip)?.clone();
let operands = &line.operands;
let instruction = line.instruction;
let result = instruction.execute(self, operands);
let was_error = if let Err(err) = result {
let msg = err.to_string();
self.state = ICState::Error(LineError {
error: err,
line: self.ip as u32,
msg,
});
self.get_circuit_holder()
.ok_or(ICError::NoCircuitHolder(self.id))?
.borrow_mut()
.as_mut_circuit_holder()
.ok_or(ICError::CircuitHolderNotLogicable(self.id))?
.set_error(1);
true
} else {
false
};
if !was_error || advance_ip_on_err {
self.ip = self.next_ip;
if self.ip >= self.program.len() && !matches!(&self.state, ICState::Error(_)) {
self.state = ICState::Ended;
}
}
self.get_circuit_holder()
.ok_or(ICError::NoCircuitHolder(self.id))?
.borrow_mut()
.as_mut_logicable()
.ok_or(ICError::CircuitHolderNotLogicable(self.id))?
.set_logic(LogicType::LineNumber, self.ip as f64, true)?;
Ok(())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,563 @@
use serde_derive::{Deserialize, Serialize};
use crate::{
errors::ICError,
interpreter::ICState,
network::Connection,
vm::{
instructions::{traits::ICInstructable, Instruction},
object::{
errors::{LogicError, MemoryError},
macros::tag_object_traits,
ObjectID, Slot, VMObject,
},
},
};
use stationeers_data::{
enums::{
basic::{Class, GasType, SortingClass},
script::{LogicSlotType, LogicType},
Species,
},
templates::RecipeOrder,
};
use std::{collections::BTreeMap, fmt::Debug};
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
use strum::{AsRefStr, Display, EnumIter, EnumProperty, EnumString, FromRepr};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ParentSlotInfo {
pub parent: ObjectID,
pub slot: usize,
}
#[derive(
Default,
Debug,
Display,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
EnumString,
AsRefStr,
EnumProperty,
EnumIter,
FromRepr,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum StatState {
#[default]
Normal,
Warning,
Critical,
}
tag_object_traits! {
#![object_trait(Object: Debug)]
pub trait Structure {
fn is_small_grid(&self) -> bool;
}
pub trait Storage {
/// Number of storage slots this object has
fn slots_count(&self) -> usize;
/// Get a reference to a indexed slot
fn get_slot(&self, index: usize) -> Option<&Slot>;
/// Get a mutable reference to a indexed slot
fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot>;
/// Get a vector of references to all an object's slots
fn get_slots(&self) -> Vec<(usize, &Slot)>;
/// Get a vector a mutable references to all an object's slots
fn get_slots_mut(&mut self) -> Vec<(usize, &mut Slot)>;
}
pub trait MemoryReadable {
/// Size of an object memory, the count of f64 elements stored
fn memory_size(&self) -> usize;
/// Get the value at the indexed memory.
/// Errors if the index over or under flows the memory
fn get_memory(&self, index: i32) -> Result<f64, MemoryError>;
/// get a slice of the objects' memory
fn get_memory_slice(&self) -> &[f64];
}
pub trait MemoryWritable: MemoryReadable {
/// Set the value at the indexed memory
/// Errors if the index over or under flows the memory
fn set_memory(&mut self, index: i32, val: f64) -> Result<(), MemoryError>;
/// Reset all an object's memory (typically to all zero values)
fn clear_memory(&mut self);
}
pub trait Logicable: Storage {
/// The crc32 hash of the object's prefab name
fn prefab_hash(&self) -> i32;
/// The crc32 hash of an object's name
fn name_hash(&self) -> i32;
/// If the object has *any* readable logic fields
fn is_logic_readable(&self) -> bool;
/// If the object has *any* writable logic fields
fn is_logic_writeable(&self) -> bool;
/// Can the logic type be read form this object
fn can_logic_read(&self, lt: LogicType) -> bool;
/// Can the logic type be written to this object
fn can_logic_write(&self, lt: LogicType) -> bool;
/// Write the value of the logic type on this object.
/// Errors if the type can not be written to.
/// force will allow special cases for existing values that arn't
/// normally writable.
/// This is for use outside of ic10 code but does not guarantee the
/// value will write or that no error will result.
/// If a logic type is not present on an object the force write will still error.
fn set_logic(&mut self, lt: LogicType, value: f64, force: bool) -> Result<(), LogicError>;
/// Read the value of the logic type on this object.
/// Errors if a logic type is not readable
fn get_logic(&self, lt: LogicType) -> Result<f64, LogicError>;
/// Can a slot logic type be read from the indexed slot
fn can_slot_logic_read(&self, slt: LogicSlotType, index: f64) -> bool;
/// Read a slot logic type value from an index slot
fn get_slot_logic(&self, slt: LogicSlotType, index: f64) -> Result<f64, LogicError>;
/// Returns a vector of the `LogicType`'s that could be read or written to or form this
/// object
fn valid_logic_types(&self) -> Vec<LogicType>;
/// If this object has modes returns a vector of (value, name) pairs
fn known_modes(&self) -> Option<Vec<(u32, String)>>;
}
pub trait SourceCode {
/// Set the source code for this object.
/// Errors if the source code has compilation errors.
fn set_source_code(&mut self, code: &str) -> Result<(), ICError>;
/// Set the source code for this object, lines that fail to compile are reduced to nops.
fn set_source_code_with_invalid(&mut self, code: &str);
/// Return the source code form this object
fn get_source_code(&self) -> String;
/// Return the compiled instruction and it's operands at the indexed line in the source
/// code.
fn get_line(&self, line: usize) -> Result<Instruction, ICError>;
/// Return a vector of any errors encountered while compiling the source with `set_source_code_with_invalid`
fn get_compile_errors(&self) -> Vec<ICError>;
}
pub trait CircuitHolder: Logicable + Storage {
/// Clear any error set on the circuit holder
fn clear_error(&mut self);
/// Set an int error value on the circuit holder
fn set_error(&mut self, state: i32);
/// Get a reference (which may be self) to a logicable object based on an index
/// (`db`, `d0`, `d1` etc.).
/// for a StructureCircuitHolder this would be the set pins
/// fpr a tablet or suit this would be the parent human's equipment
/// i32::MAX is db
fn get_logicable_from_index(
&self,
device: i32,
connection: Option<usize>,
) -> Option<ObjectRef>;
/// Get a mutable reference (which may be self) to a logicable object based on an index
/// (`db`, `d0`, `d1` etc.).
/// for a StructureCircuitHolder this would be the set pins
/// fpr a tablet or suit this would be the parent human's equipment
/// i32::MAX is db
fn get_logicable_from_index_mut(
&mut self,
device: i32,
connection: Option<usize>,
) -> Option<ObjectRefMut>;
/// Use an object id to get a reference to an object network visible object.
/// uses ObjectRef in case the object ID is it's own ID
fn get_logicable_from_id(
&self,
device: ObjectID,
connection: Option<usize>,
) -> Option<ObjectRef>;
/// Use an object id to get a mutable reference to an object network visible object.
/// uses ObjectRefMut in case the object ID is it's own ID
fn get_logicable_from_id_mut(
&mut self,
device: ObjectID,
connection: Option<usize>,
) -> Option<ObjectRefMut>;
/// Get the programmable circuit object slotted into this circuit holder
fn get_ic(&self) -> Option<VMObject>;
/// Execute a `hcf` instruction
fn halt_and_catch_fire(&mut self);
}
pub trait Item {
/// Is an item consumable?
fn consumable(&self) -> bool;
/// If an item is a filter what gas is it for?
fn filter_type(&self) -> Option<GasType>;
/// Is this item an ingredient ?
fn ingredient(&self) -> bool;
/// The max quantity this item stacks to
fn max_quantity(&self) -> u32;
/// Map of the reagents to the quantity produces by processing this item
fn reagents(&self) -> Option<&BTreeMap<String, f64>>;
/// The class of item this is for storage slots
fn slot_class(&self) -> Class;
/// The sorting class of the item
fn sorting_class(&self) -> SortingClass;
/// The parent object and slot index this item is stored in
fn get_parent_slot(&self) -> Option<ParentSlotInfo>;
/// Set the parent object and slot index this object is stored in
fn set_parent_slot(&mut self, info: Option<ParentSlotInfo>);
/// Get the damage 0.0 is no damage, 1.0 is full damage
fn get_damage(&self) -> f32;
/// Set the damage of the object, 0.0 is no damage, 1.0 is full damage
fn set_damage(&mut self, damage: f32);
/// If this object is stored in a human's inventory or in an inventory down the chain from
/// a human, return that human
fn root_parent_human(&self) -> Option<VMObject> {
self.get_parent_slot().and_then(|info| {
if let Some(obj) = self.get_vm().get_object(info.parent) {
if obj.borrow().as_human().is_some() {
return Some(obj);
}
let obj_ref = obj.borrow();
if let Some(item) = obj_ref.as_item() {
return item.root_parent_human()
}
}
None
})
}
}
pub trait Plant {
fn get_efficiency(&self) -> f64;
fn get_health(&self) -> f64;
fn get_growth(&self) -> f64;
fn is_mature(&self) -> bool;
fn is_seeding(&self) -> bool;
}
pub trait Suit: Item + Storage {
fn pressure_waste(&self) -> f32;
fn pressure_waste_max(&self) -> f32;
fn pressure_air(&self) -> f32;
}
pub trait InternalAtmosphere {
fn get_volume(&self) -> f64;
}
pub trait Thermal {
fn get_convection_factor(&self) -> f32;
fn get_radiation_factor(&self) -> f32;
}
pub trait IntegratedCircuit: Logicable + MemoryWritable + SourceCode + Item {
/// Get the object that acts as the circuit holder for this object
fn get_circuit_holder(&self) -> Option<VMObject>;
/// Get the current instruction pointer
fn get_instruction_pointer(&self) -> u32;
/// Set the next instruction to execute. The instruction pointer is set to this value once
/// execution of the current instruction is complete.
fn set_next_instruction(&mut self, next_instruction: f64);
/// Set the next instruction to execute relative to the current instruction pointer.
/// The instruction pointer is set to this value once execution of the current
/// instruction is complete.
fn set_next_instruction_relative(&mut self, offset: f64) {
self.set_next_instruction(self.get_instruction_pointer() as f64 + offset);
}
/// Reset the circuit. The instruction pointer, instruction count since last yield, all
/// registers and memory are set to 0; aliases and defines are cleared; state is set back
/// to start.
fn reset(&mut self);
/// When given some indirection level and a first target read registers values as
/// targets while reducing indirection level by one until it reaches to 0
/// to find the real target.
/// Errors if any index along the chain is out of range
fn get_real_target(&self, indirection: u32, target: u32) -> Result<f64, ICError>;
/// Return a register value through possible indirection
/// Errors if any index along the chain is out of range
fn get_register(&self, indirection: u32, target: u32) -> Result<f64, ICError>;
/// Get a slice of all registers
fn get_registers(&self) -> &[f64];
/// Get a mutable slice of all registers
fn get_registers_mut(&mut self) -> &mut [f64];
/// Set a register value through possible indirection
/// Errors if any index along the chain is out of range
fn set_register(&mut self, indirection: u32, target: u32, val: f64) -> Result<f64, ICError>;
/// Set the return address register's value
fn set_return_address(&mut self, addr: f64);
/// Set the return address to the instruction after the current instruction pointer
fn al(&mut self) {
self.set_return_address(self.get_instruction_pointer() as f64 + 1.0);
}
/// Write value to the stack memory at the current stack pointer and advance stack pointer
/// Errors for stack under or overflow of the stack pointer
fn push_stack(&mut self, val: f64) -> Result<f64, ICError>;
/// Read value from the stack memory at the current stack pointer and decrement the stack pointer
/// Errors for stack under or overflow of the stack pointer
fn pop_stack(&mut self) -> Result<f64, ICError>;
/// Read the value form the stack memory at the current stack pointer and leave the stack pointer
/// at the same location
/// Errors for stack under or overflow of the stack pointer
fn peek_stack(&self) -> Result<f64, ICError>;
/// Read the value from the stack memory at indexed address
/// Errors for stack under or overflow of the address
fn get_stack(&self, addr: f64) -> Result<f64, ICError>;
/// Write the value to the stack memory at the indexed address
/// Errors for stack under or overflow of the address
fn put_stack(&mut self, addr: f64, val: f64) -> Result<f64, ICError>;
/// Get a reference to the alias Map
fn get_aliases(&self) -> &BTreeMap<String, crate::vm::instructions::operands::Operand>;
/// Get a mutable reference to the alias Map
fn get_aliases_mut(&mut self) -> &mut BTreeMap<String, crate::vm::instructions::operands::Operand>;
/// Get a reference to the define Map
fn get_defines(&self) -> &BTreeMap<String, f64>;
/// Get a mutable reference to the define Map
fn get_defines_mut(&mut self) -> &mut BTreeMap<String, f64>;
/// Get a reference to the labels Map
fn get_labels(&self) -> &BTreeMap<String, u32>;
/// Get the current circuit state. (Start, Yield, Sleep, Error, etc.)
fn get_state(&self) -> ICState;
/// Set the current circuit state. (Start, Yield, Sleep, Error, etc.)
fn set_state(&mut self, state: ICState);
/// Get the count of instructions executed since the last yield
fn get_instructions_since_yield(&self) -> u16;
}
pub trait Programmable: ICInstructable {
fn step(&mut self, advance_ip_on_err: bool) -> Result<(), crate::errors::ICError>;
}
pub trait Chargeable {
fn get_charge(&self) -> f32;
fn set_charge(&mut self, charge: f32);
fn get_max_charge(&self) -> f32;
fn get_charge_ratio(&self) -> f32 {
self.get_charge() / self.get_max_charge()
}
fn get_charge_delta(&self) -> f32 {
self.get_charge() - self.get_max_charge()
}
fn is_empty(&self) -> bool {
self.get_charge() == 0.0
}
}
pub trait Instructable: MemoryWritable {
// fn get_instructions(&self) -> Vec<LogicInstruction>
}
pub trait LogicStack: MemoryWritable {
// fn logic_stack(&self) -> LogicStack;
}
pub trait Device: Logicable {
/// Can the slot logic type be written to the object at the indexed slot
fn can_slot_logic_write(&self, slt: LogicSlotType, index: f64) -> bool;
/// Write to the slot logic type at the indexed slot
/// Errors if the index is out of range or the slot logic type is not writable
fn set_slot_logic(
&mut self,
slt: LogicSlotType,
index: f64,
value: f64,
force: bool,
) -> Result<(), LogicError>;
/// Get a slice of the Device's network connections
fn connection_list(&self) -> &[Connection];
/// Get a mutable slice of the Device's network connections
fn connection_list_mut(&mut self) -> &mut [Connection];
/// Get a slice of the devices "pins" (connected object Ids) if the device has pins
fn device_pins(&self) -> Option<&[Option<ObjectID>]>;
/// Get a mutable slice of the devices "pins" (connected object Ids) if the device has pins
fn device_pins_mut(&mut self) -> Option<&mut [Option<ObjectID>]>;
/// Does the device respond to Activate
fn has_activate_state(&self) -> bool;
/// Does the device have an internal atmosphere
fn has_atmosphere(&self) -> bool;
/// Does the device have a Color state
fn has_color_state(&self) -> bool;
/// Does the device have a Lock state
fn has_lock_state(&self) -> bool;
/// Does the device have a mode state
fn has_mode_state(&self) -> bool;
/// Does the device have an On / off state
fn has_on_off_state(&self) -> bool;
/// Does the device have an Open state
fn has_open_state(&self) -> bool;
/// Does the device store reagents
fn has_reagents(&self) -> bool;
/// Return vector of (reagent_hash, quantity) pairs
fn get_reagents(&self) -> Vec<(u8, f64)>;
/// Overwrite present reagents
fn set_reagents(&mut self, reagents: &[(u8, f64)]);
/// Adds the reagents to contents
fn add_reagents(&mut self, reagents: &[(u8, f64)]);
}
pub trait ReagentConsumer {
fn can_process_reagent(&self, reagent: u8) -> bool;
fn get_resources_used(&self) -> Vec<i32>;
}
pub trait ReagentRequirer: Device {
/// the currently selected Recipe and Order
fn get_current_recipe(&self) -> Option<RecipeOrder>;
/// Reagents required to complete current recipe
fn get_current_required(&self) -> Vec<(u8, f64)>;
/// Map Reagent hash to Prefab Hash
fn get_prefab_hash_from_reagent_hash(&self, reagent_hash: i32) -> Option<i32>;
}
pub trait Fabricator: ReagentRequirer {
}
pub trait WirelessTransmit: Logicable {}
pub trait WirelessReceive: Logicable {}
pub trait Network: Logicable {
/// Does the network contain the Object id
fn contains(&self, id: &ObjectID) -> bool;
/// Does the network contain all the object ids
fn contains_all(&self, ids: &[ObjectID]) -> bool;
/// Does the network contain the object id on a data connection
fn contains_data(&self, id: &ObjectID) -> bool;
/// Does the network contain all the object ids on a data connection
fn contains_all_data(&self, ids: &[ObjectID]) -> bool;
/// Does the network contain the object id on a power connection
fn contains_power(&self, id: &ObjectID) -> bool;
/// Does the network contain all the object ids on a power connection
fn contains_all_power(&self, ids: &[ObjectID]) -> bool;
/// Return a vector of all object ids visible to the data connection of the source ID object
fn data_visible(&self, source: &ObjectID) -> Vec<u32>;
/// Add the object to the network as a data connection
fn add_data(&mut self, id: ObjectID) -> bool;
/// Add the object id as a power connection
fn add_power(&mut self, id: ObjectID) -> bool;
/// remove the object id for both power and data connections if present in either
fn remove_all(&mut self, id: ObjectID) -> bool;
/// remove the object id from data network
fn remove_data(&mut self, id: ObjectID) -> bool;
/// remove object id from power network
fn remove_power(&mut self, id: ObjectID) -> bool;
/// get all data connected devices
fn get_devices(&self) -> Vec<ObjectID>;
/// get all power connected devices
fn get_power_only(&self) -> Vec<ObjectID>;
/// get a slice of the channel data values
fn get_channel_data(&self) -> &[f64; 8];
}
pub trait Human : Storage {
fn get_species(&self) -> Species;
fn get_damage(&self) -> f32;
fn set_damage(&mut self, damage: f32);
fn get_nutrition(&self) -> f32;
fn set_nutrition(&mut self, nutrition: f32);
fn nutrition_state(&self) -> StatState;
fn get_hydration(&self) -> f32;
fn set_hydration(&mut self, hydration: f32);
fn hydration_state(&self) -> StatState;
fn get_oxygenation(&self) -> f32;
fn set_oxygenation(&mut self, oxygenation: f32);
fn get_food_quality(&self) -> f32;
fn set_food_quality(&mut self, quality: f32);
fn get_mood(&self) -> f32;
fn set_mood(&mut self, mood: f32);
fn mood_state(&self) -> StatState;
fn get_hygiene(&self) -> f32;
fn set_hygiene(&mut self, hygiene: f32);
fn hygiene_state(&self) -> StatState;
fn is_artificial(&self) -> bool;
fn robot_battery(&self) -> Option<VMObject>;
fn suit_slot(&self) -> &Slot;
fn suit_slot_mut(&mut self) -> &mut Slot;
fn helmet_slot(&self) -> &Slot;
fn helmet_slot_mut(&mut self) -> &mut Slot;
fn glasses_slot(&self) -> &Slot;
fn glasses_slot_mut(&mut self) -> &mut Slot;
fn backpack_slot(&self) -> &Slot;
fn backpack_slot_mut(&mut self) -> &mut Slot;
fn left_hand_slot(&self) -> &Slot;
fn left_hand_slot_mut(&mut self) -> &mut Slot;
fn right_hand_slot(&self) -> &Slot;
fn right_hand_slot_mut(&mut self) -> &mut Slot;
fn uniform_slot(&self) -> &Slot;
fn uniform_slot_mut(&mut self) -> &mut Slot;
fn toolbelt_slot(&self) -> &Slot;
fn toolbelt_slot_mut(&mut self) -> &mut Slot;
}
}
impl Debug for dyn Object {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Object{{id: {:?}, type: {}, interfaces: {:?}}}",
self.get_id(),
self.type_name(),
ObjectInterfaces::from_object(self),
)
}
}
impl<T: CircuitHolder> SourceCode for T {
fn debug_source_code(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "source_code: {:?}", self.get_source_code())
}
fn get_line(&self, line: usize) -> Result<Instruction, ICError> {
let ic = self.get_ic().ok_or(ICError::DeviceHasNoIC)?;
let result = ic
.borrow()
.as_source_code()
.ok_or(ICError::DeviceHasNoIC)?
.get_line(line);
result.clone()
}
fn set_source_code(&mut self, code: &str) -> Result<(), ICError> {
self.get_ic()
.and_then(|obj| {
obj.borrow_mut()
.as_mut_source_code()
.map(|source| source.set_source_code(code))
})
.transpose()?;
Ok(())
}
fn set_source_code_with_invalid(&mut self, code: &str) {
self.get_ic().and_then(|obj| {
obj.borrow_mut()
.as_mut_source_code()
.map(|source| source.set_source_code_with_invalid(code))
});
}
fn get_source_code(&self) -> String {
self.get_ic()
.and_then(|obj| {
obj.borrow()
.as_source_code()
.map(|source| source.get_source_code())
})
.unwrap_or_default()
}
fn get_compile_errors(&self) -> Vec<ICError> {
self.get_ic()
.and_then(|obj| {
obj.borrow()
.as_source_code()
.map(|source| source.get_compile_errors())
})
.unwrap_or_default()
}
}

View File

@@ -5,30 +5,59 @@ version.workspace = true
edition.workspace = true
[dependencies]
ic10emu = { path = "../ic10emu" }
console_error_panic_hook = {version = "0.1.7", optional = true}
stationeers_data = { path = "../stationeers_data", features = ["tsify"] }
ic10emu = { path = "../ic10emu", features = ["tsify"] }
console_error_panic_hook = { version = "0.1.7", optional = true }
js-sys = "0.3.69"
web-sys = { version = "0.3.69", features = ["WritableStream", "console"] }
wasm-bindgen = "0.2.81"
wasm-bindgen-futures = { version = "0.4.30", features = [
wasm-bindgen = "0.2.92"
wasm-bindgen-futures = { version = "0.4.42", features = [
"futures-core-03-stream",
] }
wasm-streams = "0.4"
serde-wasm-bindgen = "0.6.5"
itertools = "0.12.1"
serde = { version = "1.0.197", features = ["derive"] }
serde_with = "3.7.0"
tsify = { version = "0.4.5", default-features = false, features = ["js", "wasm-bindgen"] }
thiserror = "1.0.58"
[build-dependencies]
ic10emu = { path = "../ic10emu" }
strum = { version = "0.26.2"}
itertools = "0.12.1"
serde_path_to_error = "0.1.16"
serde_ignored = "0.1.10"
color-eyre = "0.6.3"
itertools = "0.13.0"
serde = { version = "1.0.202", features = ["derive"] }
serde_with = "3.8.1"
tsify = { version = "0.4.5", features = ["js"] }
thiserror = "1.0.61"
serde_derive = "1.0.203"
serde_json = "1.0.117"
tracing-wasm = "0.2.1"
tracing = "0.1.40"
[features]
default = ["console_error_panic_hook"]
console_error_panic_hook = ["dep:console_error_panic_hook"]
prefab_database = [
"ic10emu/prefab_database",
"stationeers_data/prefab_database",
]
[lib]
crate-type = ["cdylib", "rlib"]
[package.metadata.wasm-pack.profile.dev]
wasm-opt = ['-O']
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
# Should we enable wasm-bindgen's debug assertions in its generated JS glue?
debug-js-glue = true
# Should wasm-bindgen demangle the symbols in the "name" custom section?
demangle-name-section = true
# Should we emit the DWARF debug info custom sections?
dwarf-debug-info = false
# Should we omit the default import path?
omit-default-module-path = false
[package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Os']
[package.metadata.wasm-pack.profile.release.wasm-bindgen]
debug-js-glue = false
demangle-name-section = true
dwarf-debug-info = false
omit-default-module-path = false

View File

@@ -1,96 +0,0 @@
use std::{
env,
fs::{self, File},
io::{BufWriter, Write},
path::Path,
};
use itertools::Itertools;
use strum::IntoEnumIterator;
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("ts_types.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut ts_types: String = String::new();
let lt_tsunion: String = Itertools::intersperse(
ic10emu::grammar::generated::LogicType::iter().map(|lt| format!("\"{}\"", lt.as_ref())),
"\n | ".to_owned(),
)
.collect();
let lt_tstype = format!("\nexport type LogicType = {};", lt_tsunion);
ts_types.push_str(&lt_tstype);
let slt_tsunion: String = Itertools::intersperse(
ic10emu::grammar::generated::SlotLogicType::iter().map(|slt| format!("\"{}\"", slt.as_ref())),
"\n | ".to_owned(),
)
.collect();
let slt_tstype = format!("\nexport type SlotLogicType = {};", slt_tsunion);
ts_types.push_str(&slt_tstype);
let bm_tsunion: String = Itertools::intersperse(
ic10emu::grammar::generated::BatchMode::iter().map(|bm| format!("\"{}\"", bm.as_ref())),
"\n | ".to_owned(),
)
.collect();
let bm_tstype = format!("\nexport type BatchMode = {};", bm_tsunion);
ts_types.push_str(&bm_tstype);
let rm_tsunion: String = Itertools::intersperse(
ic10emu::grammar::generated::ReagentMode::iter().map(|rm| format!("\"{}\"", rm.as_ref())),
"\n | ".to_owned(),
)
.collect();
let rm_tstype = format!("\nexport type ReagentMode = {};", rm_tsunion);
ts_types.push_str(&rm_tstype);
let sc_tsunion: String = Itertools::intersperse(
ic10emu::device::SortingClass::iter().map(|rm| format!("\"{}\"", rm.as_ref())),
"\n | ".to_owned(),
)
.collect();
let sc_tstype = format!("\nexport type SortingClass = {};", sc_tsunion);
ts_types.push_str(&sc_tstype);
let st_tsunion: String = Itertools::intersperse(
ic10emu::device::SlotType::iter().map(|rm| format!("\"{}\"", rm.as_ref())),
"\n | ".to_owned(),
)
.collect();
let st_tstype = format!("\nexport type SlotType = {};", st_tsunion);
ts_types.push_str(&st_tstype);
let ct_tsunion: String = Itertools::intersperse(
ic10emu::network::ConnectionType::iter().map(|rm| format!("\"{}\"", rm.as_ref())),
"\n | ".to_owned(),
)
.collect();
let ct_tstype = format!("\nexport type ConnectionType = {};", ct_tsunion);
ts_types.push_str(&ct_tstype);
let cr_tsunion: String = Itertools::intersperse(
ic10emu::network::ConnectionRole::iter().map(|rm| format!("\"{}\"", rm.as_ref())),
"\n | ".to_owned(),
)
.collect();
let cr_tstype = format!("\nexport type ConnectionRole = {};", cr_tsunion);
ts_types.push_str(&cr_tstype);
let infile = Path::new("src/types.ts");
let contents = fs::read_to_string(infile).unwrap();
ts_types.push('\n');
ts_types.push_str(&contents);
write!(
&mut writer,
"#[wasm_bindgen(typescript_custom_section)]\n\
const TYPES: &'static str = r#\"{ts_types}\"#;
"
)
.unwrap();
}

View File

@@ -1,20 +1,28 @@
#[macro_use]
mod utils;
mod types;
// mod types;
use ic10emu::{
device::{Device, DeviceTemplate, SlotOccupantTemplate},
grammar::{LogicType, SlotLogicType},
vm::{FrozenVM, VMError, VM},
errors::{ICError, TemplateError, VMError},
network::FrozenCableNetwork,
vm::{
object::{
templates::{FrozenObject, FrozenObjectFull},
ObjectID,
},
FrozenVM, VM,
},
};
use serde::{Deserialize, Serialize};
use types::{Registers, Stack};
use std::{cell::RefCell, rc::Rc, str::FromStr};
use itertools::Itertools;
// use std::iter::FromIterator;
// use itertools::Itertools;
use serde_derive::{Deserialize, Serialize};
use stationeers_data::{
enums::script::{LogicSlotType, LogicType},
templates::{ObjectTemplate, Reagent},
};
use std::{collections::BTreeMap, rc::Rc};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
@@ -22,12 +30,6 @@ extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub struct DeviceRef {
device: Rc<RefCell<Device>>,
vm: Rc<RefCell<VM>>,
}
use thiserror::Error;
#[derive(Error, Debug, Serialize, Deserialize)]
@@ -38,481 +40,451 @@ pub enum BindingError {
OutOfBounds(usize, usize),
}
#[wasm_bindgen]
impl DeviceRef {
fn from_device(device: Rc<RefCell<Device>>, vm: Rc<RefCell<VM>>) -> Self {
DeviceRef { device, vm }
}
#[wasm_bindgen(getter)]
pub fn id(&self) -> u32 {
self.device.borrow().id
}
#[wasm_bindgen(getter)]
pub fn ic(&self) -> Option<u32> {
self.device.borrow().ic
}
#[wasm_bindgen(getter)]
pub fn name(&self) -> Option<String> {
self.device.borrow().name.clone()
}
#[wasm_bindgen(getter, js_name = "nameHash")]
pub fn name_hash(&self) -> Option<i32> {
self.device.borrow().name_hash
}
#[wasm_bindgen(getter, js_name = "prefabName")]
pub fn prefab_name(&self) -> Option<String> {
self.device
.borrow()
.prefab
.as_ref()
.map(|prefab| prefab.name.clone())
}
#[wasm_bindgen(getter, js_name = "prefabHash")]
pub fn prefab_hash(&self) -> Option<i32> {
self.device
.borrow()
.prefab
.as_ref()
.map(|prefab| prefab.hash)
}
#[wasm_bindgen(getter, skip_typescript)]
pub fn fields(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().get_fields(&self.vm.borrow())).unwrap()
}
#[wasm_bindgen(getter)]
pub fn slots(&self) -> types::Slots {
types::Slots::from_iter(self.device.borrow().slots.iter())
}
#[wasm_bindgen(getter, skip_typescript)]
pub fn reagents(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().reagents).unwrap()
}
#[wasm_bindgen(getter, skip_typescript)]
pub fn connections(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().connections).unwrap()
}
#[wasm_bindgen(getter, js_name = "ip")]
pub fn ic_ip(&self) -> Option<u32> {
self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().ip())
})
}
#[wasm_bindgen(getter, js_name = "instructionCount")]
pub fn ic_instruction_count(&self) -> Option<u16> {
self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().ic.get())
})
}
#[wasm_bindgen(getter, js_name = "stack")]
pub fn ic_stack(&self) -> Option<Stack> {
self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| Stack(*ic.as_ref().borrow().stack.borrow()))
})
}
#[wasm_bindgen(getter, js_name = "registers")]
pub fn ic_registers(&self) -> Option<Registers> {
self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| Registers(*ic.as_ref().borrow().registers.borrow()))
})
}
#[wasm_bindgen(getter, js_name = "aliases", skip_typescript)]
pub fn ic_aliases(&self) -> JsValue {
let aliases = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().aliases.borrow().clone())
});
serde_wasm_bindgen::to_value(aliases).unwrap()
}
#[wasm_bindgen(getter, js_name = "defines", skip_typescript)]
pub fn ic_defines(&self) -> JsValue {
let defines = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().defines.borrow().clone())
});
serde_wasm_bindgen::to_value(defines).unwrap()
}
#[wasm_bindgen(getter, js_name = "pins", skip_typescript)]
pub fn ic_pins(&self) -> JsValue {
let pins = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| *ic.as_ref().borrow().pins.borrow())
});
serde_wasm_bindgen::to_value(pins).unwrap()
}
#[wasm_bindgen(getter, js_name = "state")]
pub fn ic_state(&self) -> Option<String> {
self.device
.borrow()
.ic
.as_ref()
.and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.borrow().state.clone())
})
.map(|state| state.borrow().to_string())
}
#[wasm_bindgen(getter, js_name = "program", skip_typescript)]
pub fn ic_program(&self) -> JsValue {
let prog = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.borrow().program.borrow().clone())
});
serde_wasm_bindgen::to_value(prog).unwrap()
}
#[wasm_bindgen(getter, js_name = "code")]
pub fn get_code(&self) -> Option<String> {
self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.borrow().code.borrow().clone())
})
}
#[wasm_bindgen(js_name = "step")]
pub fn step_ic(&self, advance_ip_on_err: bool) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self.vm.borrow().step_ic(id, advance_ip_on_err)?)
}
#[wasm_bindgen(js_name = "run")]
pub fn run_ic(&self, ignore_errors: bool) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self.vm.borrow().run_ic(id, ignore_errors)?)
}
#[wasm_bindgen(js_name = "reset")]
pub fn reset_ic(&self) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self.vm.borrow().reset_ic(id)?)
}
#[wasm_bindgen(js_name = "setCode")]
/// Set program code if it's valid
pub fn set_code(&self, code: &str) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self.vm.borrow().set_code(id, code)?)
}
#[wasm_bindgen(js_name = "setCodeInvalid")]
/// Set program code and translate invalid lines to Nop, collecting errors
pub fn set_code_invlaid(&self, code: &str) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self.vm.borrow().set_code_invalid(id, code)?)
}
#[wasm_bindgen(js_name = "setRegister")]
pub fn ic_set_register(&self, index: u32, val: f64) -> Result<f64, JsError> {
let ic_id = *self
.device
.borrow()
.ic
.as_ref()
.ok_or(VMError::NoIC(self.device.borrow().id))?;
let vm_borrow = self.vm.borrow();
let ic = vm_borrow
.ics
.get(&ic_id)
.ok_or(VMError::NoIC(self.device.borrow().id))?;
let result = ic.borrow_mut().set_register(0, index, val)?;
Ok(result)
}
#[wasm_bindgen(js_name = "setStack")]
pub fn ic_set_stack(&self, address: f64, val: f64) -> Result<f64, JsError> {
let ic_id = *self
.device
.borrow()
.ic
.as_ref()
.ok_or(VMError::NoIC(self.device.borrow().id))?;
let vm_borrow = self.vm.borrow();
let ic = vm_borrow
.ics
.get(&ic_id)
.ok_or(VMError::NoIC(self.device.borrow().id))?;
let result = ic.borrow_mut().poke(address, val)?;
Ok(result)
}
#[wasm_bindgen(js_name = "setName")]
pub fn set_name(&self, name: &str) {
self.device.borrow_mut().set_name(name)
}
#[wasm_bindgen(js_name = "setField", skip_typescript)]
pub fn set_field(&self, field: &str, value: f64, force: bool) -> Result<(), JsError> {
let logic_typ = LogicType::from_str(field)?;
let mut device_ref = self.device.borrow_mut();
device_ref.set_field(logic_typ, value, &self.vm.borrow(), force)?;
Ok(())
}
#[wasm_bindgen(js_name = "setSlotField", skip_typescript)]
pub fn set_slot_field(
&self,
slot: f64,
field: &str,
value: f64,
force: bool,
) -> Result<(), JsError> {
let logic_typ = SlotLogicType::from_str(field)?;
let mut device_ref = self.device.borrow_mut();
device_ref.set_slot_field(slot, logic_typ, value, &self.vm.borrow(), force)?;
Ok(())
}
#[wasm_bindgen(js_name = "getSlotField", skip_typescript)]
pub fn get_slot_field(&self, slot: f64, field: &str) -> Result<f64, JsError> {
let logic_typ = SlotLogicType::from_str(field)?;
let device_ref = self.device.borrow_mut();
Ok(device_ref.get_slot_field(slot, logic_typ, &self.vm.borrow())?)
}
#[wasm_bindgen(js_name = "getSlotFields", skip_typescript)]
pub fn get_slot_fields(&self, slot: f64) -> Result<JsValue, JsError> {
let device_ref = self.device.borrow_mut();
let fields = device_ref.get_slot_fields(slot, &self.vm.borrow())?;
Ok(serde_wasm_bindgen::to_value(&fields).unwrap())
}
#[wasm_bindgen(js_name = "setConnection")]
pub fn set_connection(&self, conn: usize, net: Option<u32>) -> Result<(), JsError> {
let device_id = self.device.borrow().id;
self.vm
.borrow()
.set_device_connection(device_id, conn, net)?;
Ok(())
}
#[wasm_bindgen(js_name = "removeDeviceFromNetwork")]
pub fn remove_device_from_network(&self, network_id: u32) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self
.vm
.borrow()
.remove_device_from_network(id, network_id)?)
}
#[wasm_bindgen(js_name = "setPin")]
pub fn set_pin(&self, pin: usize, val: Option<u32>) -> Result<bool, JsError> {
let id = self.device.borrow().id;
Ok(self.vm.borrow().set_pin(id, pin, val)?)
}
}
#[wasm_bindgen]
#[derive(Debug)]
pub struct VMRef {
vm: Rc<RefCell<VM>>,
vm: Rc<VM>,
}
use tsify::Tsify;
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct TemplateDatabase(BTreeMap<i32, ObjectTemplate>);
impl IntoIterator for TemplateDatabase {
type Item = (i32, ObjectTemplate);
type IntoIter = std::collections::btree_map::IntoIter<i32, ObjectTemplate>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct ReagentDatabase(BTreeMap<u8, Reagent>);
impl IntoIterator for ReagentDatabase {
type Item = (u8, Reagent);
type IntoIter = std::collections::btree_map::IntoIter<u8, Reagent>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct FrozenObjects(Vec<FrozenObjectFull>);
impl IntoIterator for FrozenObjects {
type Item = FrozenObjectFull;
type IntoIter = std::vec::IntoIter<FrozenObjectFull>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct FrozenObjectsSparse(Vec<FrozenObject>);
impl IntoIterator for FrozenObjectsSparse {
type Item = FrozenObject;
type IntoIter = std::vec::IntoIter<FrozenObject>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct FrozenNetworks(Vec<FrozenCableNetwork>);
impl IntoIterator for FrozenNetworks {
type Item = FrozenCableNetwork;
type IntoIter = std::vec::IntoIter<FrozenCableNetwork>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct CompileErrors(Vec<ICError>);
impl IntoIterator for CompileErrors {
type Item = ICError;
type IntoIter = std::vec::IntoIter<ICError>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
use color_eyre::eyre;
pub fn parse_value<'a, T: serde::Deserialize<'a>>(
jd: impl serde::Deserializer<'a>,
) -> Result<T, color_eyre::Report> {
let mut track = serde_path_to_error::Track::new();
let path = serde_path_to_error::Deserializer::new(jd, &mut track);
let mut fun = |path: serde_ignored::Path| {
tracing::warn!("Found ignored key: {path}");
};
serde_ignored::deserialize(path, &mut fun).map_err(|e| {
eyre::eyre!(
"path: {track} | error = {e}",
track = track.path().to_string(),
)
})
}
#[allow(non_snake_case)]
#[wasm_bindgen]
impl VMRef {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
VMRef {
vm: Rc::new(RefCell::new(VM::new())),
}
VMRef { vm: VM::new() }
}
#[wasm_bindgen(js_name = "addDevice")]
pub fn add_device(&self, network: Option<u32>) -> Result<u32, JsError> {
Ok(self.vm.borrow_mut().add_device(network)?)
#[wasm_bindgen(js_name = "importTemplateDatabase")]
pub fn import_template_database(&self, db: TemplateDatabase) {
self.vm.import_template_database(db);
}
#[wasm_bindgen(js_name = "addDeviceFromTemplate", skip_typescript)]
pub fn add_device_from_template(&self, template: JsValue) -> Result<u32, JsError> {
let template: DeviceTemplate = serde_wasm_bindgen::from_value(template)?;
#[wasm_bindgen(js_name = "importReagentDatabase")]
pub fn import_reagent_database(&self, db: ReagentDatabase) {
self.vm.import_reagent_database(db);
}
#[wasm_bindgen(js_name = "importTemplateDatabaseSerdeWasm")]
pub fn import_template_database_serde_wasm(&self, db: JsValue) -> Result<(), JsError> {
let parsed_db: BTreeMap<i32, ObjectTemplate> =
parse_value(serde_wasm_bindgen::Deserializer::from(db)).map_err(|err| {
<&dyn std::error::Error as std::convert::Into<JsError>>::into(
std::convert::AsRef::<dyn std::error::Error>::as_ref(&err),
)
})?;
self.vm.import_template_database(parsed_db);
Ok(())
}
#[wasm_bindgen(js_name = "importTemplateDatabaseSerdeJson")]
pub fn import_template_database_serde_json(&self, db: String) -> Result<(), JsError> {
let parsed_db: BTreeMap<i32, ObjectTemplate> =
parse_value(&mut serde_json::Deserializer::from_str(&db)).map_err(|err| {
<&dyn std::error::Error as std::convert::Into<JsError>>::into(
std::convert::AsRef::<dyn std::error::Error>::as_ref(&err),
)
})?;
self.vm.import_template_database(parsed_db);
Ok(())
}
#[wasm_bindgen(js_name = "getTemplateDatabase")]
pub fn get_template_database(&self) -> TemplateDatabase {
TemplateDatabase(self.vm.get_template_database())
}
#[wasm_bindgen(js_name = "addObjectFrozen")]
pub fn add_object_frozen(&self, frozen: FrozenObject) -> Result<ObjectID, JsError> {
web_sys::console::log_2(
&"(wasm) adding device".into(),
&serde_wasm_bindgen::to_value(&template).unwrap(),
&serde_wasm_bindgen::to_value(&frozen).unwrap(),
);
Ok(self.vm.borrow_mut().add_device_from_template(template)?)
Ok(self.vm.add_object_frozen(frozen)?)
}
#[wasm_bindgen(js_name = "getDevice")]
pub fn get_device(&self, id: u32) -> Option<DeviceRef> {
let device = self.vm.borrow().get_device(id);
device.map(|d| DeviceRef::from_device(d.clone(), self.vm.clone()))
#[wasm_bindgen(js_name = "addObjectsFrozen")]
pub fn add_objects_frozen(
&self,
frozen_objects: FrozenObjectsSparse,
) -> Result<Vec<ObjectID>, JsError> {
web_sys::console::log_2(
&"(wasm) adding device".into(),
&serde_wasm_bindgen::to_value(&frozen_objects).unwrap(),
);
Ok(self.vm.add_objects_frozen(frozen_objects)?)
}
// #[wasm_bindgen(js_name = "getDevice")]
// pub fn get_object(&self, id: ObjectID) -> Option<VMObject> {
// self.vm.get_object(id)
// }
#[wasm_bindgen(js_name = "freezeObject")]
pub fn freeze_object(&self, id: ObjectID) -> Result<FrozenObjectFull, JsError> {
Ok(self.vm.freeze_object(id)?)
}
#[wasm_bindgen(js_name = "freezeObjects")]
pub fn freeze_objects(&self, ids: Vec<ObjectID>) -> Result<FrozenObjects, JsError> {
Ok(FrozenObjects(self.vm.freeze_objects(ids)?))
}
#[wasm_bindgen(js_name = "freezeNetwork")]
pub fn freeze_network(&self, id: ObjectID) -> Result<FrozenCableNetwork, JsError> {
Ok(self.vm.freeze_network(id)?)
}
#[wasm_bindgen(js_name = "freezeNetworks")]
pub fn freeze_networks(&self, ids: Vec<ObjectID>) -> Result<FrozenNetworks, JsError> {
Ok(FrozenNetworks(self.vm.freeze_networks(ids)?))
}
#[wasm_bindgen(js_name = "setCode")]
/// Set program code if it's valid
pub fn set_code(&self, id: u32, code: &str) -> Result<bool, JsError> {
Ok(self.vm.borrow().set_code(id, code)?)
pub fn set_code(&self, id: ObjectID, code: &str) -> Result<bool, JsError> {
Ok(self.vm.set_code(id, code)?)
}
#[wasm_bindgen(js_name = "setCodeInvalid")]
/// Set program code and translate invalid lines to Nop, collecting errors
pub fn set_code_invalid(&self, id: u32, code: &str) -> Result<bool, JsError> {
Ok(self.vm.borrow().set_code_invalid(id, code)?)
pub fn set_code_invalid(&self, id: ObjectID, code: &str) -> Result<bool, JsError> {
Ok(self.vm.set_code_invalid(id, code)?)
}
#[wasm_bindgen(js_name = "stepIC")]
pub fn step_ic(&self, id: u32, advance_ip_on_err: bool) -> Result<bool, JsError> {
Ok(self.vm.borrow().step_ic(id, advance_ip_on_err)?)
#[wasm_bindgen(js_name = "getCode")]
/// Set program code if it's valid
pub fn get_code(&self, id: ObjectID) -> Result<String, JsError> {
Ok(self.vm.get_code(id)?)
}
#[wasm_bindgen(js_name = "runIC")]
pub fn run_ic(&self, id: u32, ignore_errors: bool) -> Result<bool, JsError> {
Ok(self.vm.borrow().run_ic(id, ignore_errors)?)
#[wasm_bindgen(js_name = "getCompileErrors")]
/// Set program code if it's valid
pub fn get_compiler_errors(&self, id: ObjectID) -> Result<CompileErrors, JsError> {
Ok(CompileErrors(self.vm.get_compile_errors(id)?))
}
#[wasm_bindgen(js_name = "resetIC")]
pub fn reset_ic(&self, id: u32) -> Result<bool, JsError> {
Ok(self.vm.borrow().reset_ic(id)?)
#[wasm_bindgen(js_name = "stepProgrammable")]
pub fn step_programmable(&self, id: ObjectID, advance_ip_on_err: bool) -> Result<(), JsError> {
Ok(self.vm.step_programmable(id, advance_ip_on_err)?)
}
#[wasm_bindgen(getter, js_name = "defaultNetwork")]
pub fn default_network(&self) -> u32 {
self.vm.borrow().default_network
#[wasm_bindgen(js_name = "runProgrammable")]
pub fn run_programmable(&self, id: ObjectID, ignore_errors: bool) -> Result<bool, JsError> {
Ok(self.vm.run_programmable(id, ignore_errors)?)
}
#[wasm_bindgen(getter)]
pub fn devices(&self) -> Vec<u32> {
self.vm.borrow().devices.keys().copied().collect_vec()
#[wasm_bindgen(js_name = "resetProgrammable")]
pub fn reset_ic(&self, id: ObjectID) -> Result<bool, JsError> {
Ok(self.vm.reset_programmable(id)?)
}
#[wasm_bindgen(getter)]
pub fn networks(&self) -> Vec<u32> {
self.vm.borrow().networks.keys().copied().collect_vec()
#[wasm_bindgen(js_name = "getDefaultNetwork")]
pub fn get_default_network(&self) -> ObjectID {
*self.vm.default_network_key.borrow()
}
#[wasm_bindgen(getter)]
pub fn ics(&self) -> Vec<u32> {
self.vm.borrow().ics.keys().copied().collect_vec()
#[wasm_bindgen(js_name = "getObjects")]
pub fn get_objects(&self) -> Vec<ObjectID> {
self.vm.objects.borrow().keys().copied().collect_vec()
}
#[wasm_bindgen(getter, js_name = "lastOperationModified")]
pub fn last_operation_modified(&self) -> Vec<u32> {
self.vm.borrow().last_operation_modified()
#[wasm_bindgen(js_name = "getNetworks")]
pub fn get_networks(&self) -> Vec<ObjectID> {
self.vm.networks.borrow().keys().copied().collect_vec()
}
#[wasm_bindgen(js_name = "getCircuitHolders")]
pub fn get_circuit_holders(&self) -> Vec<ObjectID> {
self.vm.circuit_holders.borrow().clone()
}
#[wasm_bindgen(js_name = "getProgramHolders")]
pub fn get_program_holders(&self) -> Vec<ObjectID> {
self.vm.program_holders.borrow().clone()
}
#[wasm_bindgen(js_name = "getLastOperationModified")]
pub fn get_last_operation_modified(&self) -> Vec<ObjectID> {
self.vm.last_operation_modified()
}
#[wasm_bindgen(js_name = "visibleDevices")]
pub fn visible_devices(&self, source: u32) -> Vec<u32> {
self.vm.borrow().visible_devices(source)
pub fn visible_devices(&self, source: ObjectID) -> Vec<u32> {
self.vm.visible_devices(source)
}
#[wasm_bindgen(js_name = "setDeviceConnection")]
pub fn set_device_connection(
&self,
id: u32,
id: ObjectID,
connection: usize,
network_id: Option<u32>,
network_id: Option<ObjectID>,
) -> Result<bool, JsError> {
Ok(self
.vm
.borrow()
.set_device_connection(id, connection, network_id)?)
Ok(self.vm.set_device_connection(id, connection, network_id)?)
}
#[wasm_bindgen(js_name = "removeDeviceFromNetwork")]
pub fn remove_device_from_network(&self, id: u32, network_id: u32) -> Result<bool, JsError> {
Ok(self
.vm
.borrow()
.remove_device_from_network(id, network_id)?)
pub fn remove_device_from_network(
&self,
id: ObjectID,
network_id: u32,
) -> Result<bool, JsError> {
Ok(self.vm.remove_device_from_network(id, network_id)?)
}
#[wasm_bindgen(js_name = "setPin")]
pub fn set_pin(&self, id: u32, pin: usize, val: Option<u32>) -> Result<bool, JsError> {
Ok(self.vm.borrow().set_pin(id, pin, val)?)
pub fn set_pin(&self, id: ObjectID, pin: usize, val: Option<u32>) -> Result<bool, JsError> {
Ok(self.vm.set_pin(id, pin, val)?)
}
#[wasm_bindgen(js_name = "changeDeviceId")]
pub fn change_device_id(&self, old_id: u32, new_id: u32) -> Result<(), JsError> {
Ok(self.vm.borrow_mut().change_device_id(old_id, new_id)?)
pub fn change_device_id(&self, old_id: ObjectID, new_id: u32) -> Result<(), JsError> {
Ok(self.vm.change_device_id(old_id, new_id)?)
}
#[wasm_bindgen(js_name = "removeDevice")]
pub fn remove_device(&self, id: u32) -> Result<(), JsError> {
Ok(self.vm.borrow_mut().remove_device(id)?)
pub fn remove_device(&self, id: ObjectID) -> Result<(), JsError> {
Ok(self.vm.remove_object(id)?)
}
#[wasm_bindgen(js_name = "setSlotOccupant", skip_typescript)]
#[wasm_bindgen(js_name = "setSlotOccupant")]
pub fn set_slot_occupant(
&self,
id: u32,
id: ObjectID,
index: usize,
template: JsValue,
) -> Result<(), JsError> {
let template: SlotOccupantTemplate = serde_wasm_bindgen::from_value(template)?;
frozen: FrozenObject,
quantity: u32,
) -> Result<Option<ObjectID>, JsError> {
let Some(prefab) = frozen.obj_info.prefab.as_ref() else {
return Err(TemplateError::MissingPrefab.into());
};
let obj_id = if let Some(obj) = frozen.obj_info.id.and_then(|id| self.vm.get_object(id)) {
// TODO: we just assume if the ID is found that the frozen object passed is the same object..
obj.get_id()
} else {
// check to see if frozen is using the same prefab as current occupant
let obj_id = if let Some(occupant_id) = {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
let obj_ref = obj.borrow();
let storage = obj_ref.as_storage().ok_or(VMError::NotStorage(id))?;
let slot = storage
.get_slot(index)
.ok_or(ICError::SlotIndexOutOfRange(index as f64))?;
slot.occupant.as_ref().map(|info| info.id)
} {
let occupant = self
.vm
.get_object(id)
.ok_or(VMError::UnknownId(occupant_id))?;
let occupant_ref = occupant.borrow();
let occupant_prefab = occupant_ref.get_prefab();
if prefab.as_str() == occupant_prefab.value.as_str() {
Some(*occupant_ref.get_id())
} else {
None
}
} else {
None
};
if let Some(obj_id) = obj_id {
obj_id
} else {
self.vm.add_object_frozen(frozen)?
}
};
Ok(self
.vm
.borrow_mut()
.set_slot_occupant(id, index, template)?)
.set_slot_occupant(id, index, Some(obj_id), quantity)?)
}
#[wasm_bindgen(js_name = "removeSlotOccupant")]
pub fn remove_slot_occupant(&self, id: u32, index: usize) -> Result<(), JsError> {
Ok(self.vm.borrow_mut().remove_slot_occupant(id, index)?)
pub fn remove_slot_occupant(
&self,
id: ObjectID,
index: usize,
) -> Result<Option<ObjectID>, JsError> {
Ok(self.vm.remove_slot_occupant(id, index)?)
}
#[wasm_bindgen(js_name = "saveVMState", skip_typescript)]
pub fn save_vm_state(&self) -> JsValue {
let state = self.vm.borrow().save_vm_state();
serde_wasm_bindgen::to_value(&state).unwrap()
#[wasm_bindgen(js_name = "saveVMState")]
pub fn save_vm_state(&self) -> Result<FrozenVM, JsError> {
Ok(self.vm.save_vm_state()?)
}
#[wasm_bindgen(js_name = "restoreVMState", skip_typescript)]
pub fn restore_vm_state(&self, state: JsValue) -> Result<(), JsError> {
let state: FrozenVM = serde_wasm_bindgen::from_value(state)?;
self.vm.borrow_mut().restore_vm_state(state)?;
#[wasm_bindgen(js_name = "restoreVMState")]
pub fn restore_vm_state(&self, state: FrozenVM) -> Result<(), JsError> {
self.vm.restore_vm_state(state)?;
Ok(())
}
#[wasm_bindgen(js_name = "getObjectName")]
pub fn get_object_name(&self, id: ObjectID) -> Result<String, JsError> {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
let name = obj.borrow().get_name().value.clone();
Ok(name)
}
#[wasm_bindgen(js_name = "setObjectName")]
pub fn set_object_name(&self, id: ObjectID, name: &str) -> Result<(), JsError> {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
obj.borrow_mut().get_mut_name().value = name.to_string();
Ok(())
}
#[wasm_bindgen(js_name = "getObjectHash")]
pub fn get_object_hash(&self, id: ObjectID) -> Result<i32, JsError> {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
let hash = obj.borrow().get_name().hash;
Ok(hash)
}
#[wasm_bindgen(js_name = "getObjectPrefabName")]
pub fn get_object_prefab_name(&self, id: ObjectID) -> Result<String, JsError> {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
let name = obj.borrow().get_prefab().value.clone();
Ok(name)
}
#[wasm_bindgen(js_name = "getObjectPrefabHash")]
pub fn get_object_prefab_hash(&self, id: ObjectID) -> Result<i32, JsError> {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
let hash = obj.borrow().get_prefab().hash;
Ok(hash)
}
#[wasm_bindgen(js_name = "getObjectSourceCode")]
pub fn get_object_source_code(&self, id: ObjectID) -> Result<Option<String>, JsError> {
let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?;
let code = obj
.borrow()
.as_source_code()
.map(|source| source.get_source_code());
Ok(code)
}
#[wasm_bindgen(js_name = "setRegister")]
pub fn set_register(&self, id: ObjectID, index: u32, val: f64) -> Result<f64, JsError> {
Ok(self.vm.set_register(id, index, val)?)
}
#[wasm_bindgen(js_name = "setMemory")]
pub fn set_memory(&self, id: ObjectID, address: u32, val: f64) -> Result<f64, JsError> {
Ok(self.vm.set_memory(id, address, val)?)
}
#[wasm_bindgen(js_name = "setLogicField")]
pub fn set_logic_field(
&self,
id: ObjectID,
lt: LogicType,
val: f64,
force: bool,
) -> Result<(), JsError> {
Ok(self.vm.set_logic_field(id, lt, val, force)?)
}
#[wasm_bindgen(js_name = "setSlotLogicField")]
pub fn set_slot_logic_field(
&self,
id: ObjectID,
slt: LogicSlotType,
index: u32,
val: f64,
force: bool,
) -> Result<(), JsError> {
Ok(self.vm.set_slot_logic_field(id, slt, index, val, force)?)
}
}
impl Default for VMRef {
@@ -524,7 +496,8 @@ impl Default for VMRef {
#[wasm_bindgen]
pub fn init() -> VMRef {
utils::set_panic_hook();
tracing_wasm::set_as_global_default();
let vm = VMRef::new();
log!("Hello from ic10emu!");
tracing::info!("Hello from ic10emu!");
vm
}

View File

@@ -1,76 +0,0 @@
#![allow(non_snake_case)]
use std::collections::BTreeMap;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use tsify::Tsify;
use wasm_bindgen::prelude::*;
#[serde_as]
#[derive(Tsify, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Stack(#[serde_as(as = "[_; 512]")] pub [f64; 512]);
#[serde_as]
#[derive(Tsify, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Registers(#[serde_as(as = "[_; 18]")] pub [f64; 18]);
#[serde_as]
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct SlotOccupant {
pub id: u32,
pub prefab_hash: i32,
pub quantity: u32,
pub max_quantity: u32,
pub damage: f64,
pub fields: BTreeMap<ic10emu::grammar::SlotLogicType, ic10emu::device::LogicField>,
}
impl From<&ic10emu::device::SlotOccupant> for SlotOccupant {
fn from(value: &ic10emu::device::SlotOccupant) -> Self {
SlotOccupant {
id: value.id,
prefab_hash: value.prefab_hash,
quantity: value.quantity,
max_quantity: value.max_quantity,
damage: value.damage,
fields: value.get_fields(),
}
}
}
#[serde_as]
#[derive(Tsify, Debug, Clone, Default, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Slot {
pub typ: ic10emu::device::SlotType,
pub occupant: Option<SlotOccupant>,
pub fields: BTreeMap<ic10emu::grammar::SlotLogicType, ic10emu::device::LogicField>,
}
impl From<&ic10emu::device::Slot> for Slot {
fn from(value: &ic10emu::device::Slot) -> Self {
Slot {
typ: value.typ,
occupant: value.occupant.as_ref().map(|occupant| occupant.into()),
fields: value.get_fields(),
}
}
}
#[serde_as]
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Slots(pub Vec<Slot>);
impl<'a> FromIterator<&'a ic10emu::device::Slot> for Slots {
fn from_iter<T: IntoIterator<Item = &'a ic10emu::device::Slot>>(iter: T) -> Self {
Slots(iter.into_iter().map(|slot| slot.into()).collect_vec())
}
}
include!(concat!(env!("OUT_DIR"), "/ts_types.rs"));

View File

@@ -1,169 +0,0 @@
export type FieldType = "Read" | "Write" | "ReadWrite";
export interface LogicField {
field_type: FieldType;
value: number;
}
export type LogicFields = Map<LogicType, LogicField>;
export type SlotLogicFields = Map<SlotLogicType, LogicField>;
export type Reagents = Map<string, Map<number, number>>;
export interface ConnectionCableNetwork {
CableNetwork: {
net: number | undefined;
typ: string;
};
}
export type Connection = ConnectionCableNetwork | "Other";
export type RegisterSpec = {
readonly RegisterSpec: {
readonly indirection: number;
readonly target: number;
};
};
export type DeviceSpec = {
readonly DeviceSpec: {
readonly device:
| "Db"
| { readonly Numbered: number }
| {
readonly Indirect: {
readonly indirection: number;
readonly target: number;
};
};
};
readonly connection: number | undefined;
};
export type OperandLogicType = { readonly LogicType: string };
export type OperandSlotLogicType = { readonly SlotLogicType: string };
export type OperandBatchMode = { readonly BatchMode: string };
export type OperandReagentMode = { readonly ReagentMode: string };
export type Identifier = { readonly Identifier: { name: string } };
export type NumberFloat = { readonly Float: number };
export type NumberBinary = { readonly Binary: BigInt };
export type NumberHexadecimal = { readonly Hexadecimal: BigInt };
export type NumberConstant = { readonly Constant: number };
export type NumberString = { readonly String: string };
export type NumberEnum = { readonly Enum: number };
export type NumberOperand = {
Number:
| NumberFloat
| NumberBinary
| NumberHexadecimal
| NumberConstant
| NumberString
| NumberEnum;
};
export type Operand =
| RegisterSpec
| DeviceSpec
| NumberOperand
| OperandLogicType
| OperandSlotLogicType
| OperandBatchMode
| OperandReagentMode
| Identifier;
export type Alias = RegisterSpec | DeviceSpec;
export type Aliases = Map<string, Alias>;
export type Defines = Map<string, number>;
export type Pins = (number | undefined)[];
export interface Instruction {
readonly instruction: string;
readonly operands: Operand[];
}
export type ICError = {
readonly ParseError: {
readonly line: number;
readonly start: number;
readonly end: number;
readonly msg: string;
};
};
export interface Program {
readonly instructions: Instruction[];
readonly errors: ICError[];
readonly labels: Map<string, number>;
}
export interface DeviceRef {
readonly fields: LogicFields;
readonly slots: Slot[];
readonly reagents: Reagents;
readonly connections: Connection[];
readonly aliases?: Aliases | undefined;
readonly defines?: Defines | undefined;
readonly pins?: Pins;
readonly program?: Program;
getSlotFields(slot: number): SlotLogicFields;
setField(field: LogicType, value: number, force: boolean): void;
setSlotField(slot: number, field: SlotLogicType, value: number, force: boolean): void;
getSlotField(slot: number, field: SlotLogicType): number;
}
export interface SlotOccupantTemplate {
id?: number;
fields: { [key in SlotLogicType]?: LogicField };
}
export interface SlotTemplate {
typ: SlotType;
occupant?: SlotOccupantTemplate;
}
export interface DeviceTemplate {
id?: number;
name?: string;
prefab_name?: string;
slots: SlotTemplate[];
// reagents: { [key: string]: float}
connections: Connection[];
fields: { [key in LogicType]?: LogicField };
}
export interface FrozenIC {
device: number;
id: number;
registers: number[];
ip: number;
ic: number;
stack: number[];
aliases: Aliases;
defines: Defines;
pins: Pins;
state: string;
code: string;
}
export interface FrozenNetwork {
id: number;
devices: number[];
power_only: number[];
channels: number[];
}
export interface FrozenVM {
ics: FrozenIC[];
devices: DeviceTemplate[];
networks: FrozenNetwork[];
default_network: number;
}
export interface VMRef {
addDeviceFromTemplate(template: DeviceTemplate): number;
setSlotOccupant(id: number, index: number, template: SlotOccupantTemplate);
saveVMState(): FrozenVM;
restoreVMState(state: FrozenVM): void;
}

View File

@@ -8,17 +8,6 @@ pub fn set_panic_hook() {
#[cfg(feature = "console_error_panic_hook")]
{
console_error_panic_hook::set_once();
web_sys::console::log_1(&format!("Panic hook set...").into());
}
}
extern crate web_sys;
// A macro to provide `println!(..)`-style syntax for `console.log` logging.
macro_rules! log {
( $( $t:tt )* ) => {
web_sys::console::log_1(&format!( $( $t )* ).into());
web_sys::console::log_1(&"Panic hook set...".into());
}
}

View File

@@ -11,19 +11,43 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
console_error_panic_hook = "0.1.7"
futures = "0.3.21"
futures = "0.3.30"
js-sys = "0.3.69"
web-sys = { version = "0.3.69", features = ["WritableStream", "console"] }
tokio = { version = "1.26.0", features = ["sync"] }
tokio = { version = "1.37.0", features = ["sync"] }
tower-lsp = { version = "0.20.0", default-features = false, features = [
"runtime-agnostic",
] }
# tree-sitter = { version = "0.9.0", package = "tree-sitter-facade" }
wasm-bindgen = "0.2.81"
wasm-bindgen-futures = { version = "0.4.30", features = [
wasm-bindgen = "0.2.92"
wasm-bindgen-futures = { version = "0.4.42", features = [
"futures-core-03-stream",
] }
wasm-streams = "0.4"
# web-tree-sitter-sys = "1.3"
ic10lsp = { git = "https://github.com/Ryex/ic10lsp.git", branch = "wasm" }
tracing-wasm = "0.2.1"
tracing = "0.1.40"
# ic10lsp = { path = "../../ic10lsp" }
[package.metadata.wasm-pack.profile.dev]
wasm-opt = ['-O']
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
# Should we enable wasm-bindgen's debug assertions in its generated JS glue?
debug-js-glue = true
# Should wasm-bindgen demangle the symbols in the "name" custom section?
demangle-name-section = true
# Should we emit the DWARF debug info custom sections?
dwarf-debug-info = false
# Should we omit the default import path?
omit-default-module-path = false
[package.metadata.wasm-pack.profile.release]
wasm-opt = ['-Oz']
[package.metadata.wasm-pack.profile.release.wasm-bindgen]
debug-js-glue = false
demangle-name-section = true
dwarf-debug-info = false
omit-default-module-path = false

View File

@@ -1,6 +1,6 @@
use futures::stream::TryStreamExt;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;
use futures::stream::TryStreamExt;
use tower_lsp::{LspService, Server};
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::stream::JsStream;
@@ -30,8 +30,9 @@ impl ServerConfig {
#[wasm_bindgen]
pub async fn serve(config: ServerConfig) -> Result<(), JsValue> {
console_error_panic_hook::set_once();
tracing_wasm::set_as_global_default();
web_sys::console::log_1(&"server::serve".into());
tracing::trace!("server::serv error:");
let ServerConfig {
into_server,
@@ -51,6 +52,7 @@ pub async fn serve(config: ServerConfig) -> Result<(), JsValue> {
})
.map_err(|err| {
web_sys::console::log_2(&"server::input Error: ".into(), &err);
tracing::error!("server::input error: {:?}", &err);
std::io::Error::from(std::io::ErrorKind::Other)
})
@@ -60,14 +62,14 @@ pub async fn serve(config: ServerConfig) -> Result<(), JsValue> {
let output = wasm_streams::WritableStream::from_raw(output);
let output = output.try_into_async_write().map_err(|err| err.0)?;
let (service, messages) = LspService::new(|client| ic10lsp_lib::server::Backend{
let (service, messages) = LspService::new(|client| ic10lsp_lib::server::Backend {
client,
files: Arc::new(RwLock::new(HashMap::new())),
config: Arc::new(RwLock::new(ic10lsp_lib::server::Configuration::default())),
});
Server::new(input, output, messages).serve(service).await;
web_sys::console::log_1(&"server::serve ic10lsp started".into());
tracing::info!("server::serve ic10lsp started");
Ok(())
}

6
rust-analyzer.json Normal file
View File

@@ -0,0 +1,6 @@
{
"rust-analyzer.cargo.features": [
"tsify",
"prefab_database"
]
}

223
rust-testing.ipynb Normal file
View File

@@ -0,0 +1,223 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
":dep stationeers_data = { path = \"./stationeers_data\" }\n",
":dep const-crc32 = \"1.3.0\"\n",
":dep color-eyre\n",
":dep serde_path_to_error\n",
":dep serde_ignored\n",
":dep serde\n",
":dep serde_json\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"use color_eyre::eyre;\n",
"pub fn parse_value<'a, T: serde::Deserialize<'a>>(\n",
" jd: impl serde::Deserializer<'a>,\n",
") -> Result<T, color_eyre::Report> {\n",
" let mut track = serde_path_to_error::Track::new();\n",
" let path = serde_path_to_error::Deserializer::new(jd, &mut track);\n",
" let mut fun = |path: serde_ignored::Path| {\n",
" eprintln!(\"Found ignored key: {path}\");\n",
" };\n",
" serde_ignored::deserialize(path, &mut fun).map_err(|e| {\n",
" eyre::eyre!(\n",
" \"path: {track} | error = {e}\",\n",
" track = track.path().to_string(),\n",
" )\n",
" })\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{7274344: StructureLogicDevice(StructureLogicDeviceTemplate { prefab: PrefabInfo { prefab_name: \"StructureAutoMinerSmall\", prefab_hash: 7274344, desc: \"The <link=Recurso><color=#0080FFFF>Recurso</color></link> SquareDig autominer is a structure that when built will mine a vertical 2x2 shaft until it hits bedrock. The autominer can be connected to a chute system, and is controllable by a logic network. Note that the autominer outputs more <link=OrePage><color=#0080FFFF>ore</color></link> than a conventional <link=ThingItemMiningDrill><color=green>Mining Drill</color></link> over the same area.\", name: \"Autominer (Small)\" }, structure: StructureInfo { small_grid: true }, thermal_info: None, internal_atmo_info: None, logic: LogicInfo { logic_slot_types: {0: {}, 1: {}}, logic_types: {Power: Read, Open: ReadWrite, Error: Read, Activate: ReadWrite, On: ReadWrite, RequiredPower: Read, ClearMemory: Write, ExportCount: Read, ImportCount: Read, PrefabHash: Read, ReferenceId: Read, NameHash: Read}, modes: None, transmission_receiver: false, wireless_logic: false, circuit_holder: false }, slots: [SlotInfo { name: \"Import\", typ: None }, SlotInfo { name: \"Export\", typ: None }], device: DeviceInfo { connection_list: [ConnectionInfo { typ: Chute, role: Input }, ConnectionInfo { typ: Chute, role: Output }, ConnectionInfo { typ: Data, role: None }, ConnectionInfo { typ: Power, role: None }], device_pins_length: None, has_activate_state: true, has_atmosphere: false, has_color_state: false, has_lock_state: false, has_mode_state: false, has_on_off_state: true, has_open_state: true, has_reagents: false } }), 111280987: ItemLogic(ItemLogicTemplate { prefab: PrefabInfo { prefab_name: \"ItemTerrainManipulator\", prefab_hash: 111280987, desc: \"0.Mode0\\n1.Mode1\", name: \"Terrain Manipulator\" }, item: ItemInfo { consumable: false, filter_type: None, ingredient: false, max_quantity: 1, reagents: None, slot_class: Tool, sorting_class: Default }, thermal_info: None, internal_atmo_info: None, logic: LogicInfo { logic_slot_types: {0: {Occupied: Read, OccupantHash: Read, Quantity: Read, Damage: Read, Charge: Read, ChargeRatio: Read, Class: Read, MaxQuantity: Read, ReferenceId: Read}, 1: {Occupied: Read, OccupantHash: Read, Quantity: Read, Damage: Read, Class: Read, MaxQuantity: Read, ReferenceId: Read}}, logic_types: {Power: Read, Mode: ReadWrite, Error: Read, Activate: ReadWrite, On: ReadWrite, ReferenceId: Read}, modes: Some({0: \"Mode0\", 1: \"Mode1\"}), transmission_receiver: false, wireless_logic: false, circuit_holder: false }, slots: [SlotInfo { name: \"Battery\", typ: Battery }, SlotInfo { name: \"Dirt Canister\", typ: Ore }] })}\n"
]
}
],
"source": [
"let entries = r#\"\n",
"{\n",
"\"7274344\": {\n",
" \"templateType\": \"StructureLogicDevice\",\n",
" \"prefab\": {\n",
" \"prefab_name\": \"StructureAutoMinerSmall\",\n",
" \"prefab_hash\": 7274344,\n",
" \"desc\": \"The <link=Recurso><color=#0080FFFF>Recurso</color></link> SquareDig autominer is a structure that when built will mine a vertical 2x2 shaft until it hits bedrock. The autominer can be connected to a chute system, and is controllable by a logic network. Note that the autominer outputs more <link=OrePage><color=#0080FFFF>ore</color></link> than a conventional <link=ThingItemMiningDrill><color=green>Mining Drill</color></link> over the same area.\",\n",
" \"name\": \"Autominer (Small)\"\n",
" },\n",
" \"structure\": {\n",
" \"small_grid\": true\n",
" },\n",
" \"logic\": {\n",
" \"logic_slot_types\": {\n",
" \"0\": {},\n",
" \"1\": {}\n",
" },\n",
" \"logic_types\": {\n",
" \"Power\": \"Read\",\n",
" \"Open\": \"ReadWrite\",\n",
" \"Error\": \"Read\",\n",
" \"Activate\": \"ReadWrite\",\n",
" \"On\": \"ReadWrite\",\n",
" \"RequiredPower\": \"Read\",\n",
" \"ClearMemory\": \"Write\",\n",
" \"ExportCount\": \"Read\",\n",
" \"ImportCount\": \"Read\",\n",
" \"PrefabHash\": \"Read\",\n",
" \"ReferenceId\": \"Read\",\n",
" \"NameHash\": \"Read\"\n",
" },\n",
" \"transmission_receiver\": false,\n",
" \"wireless_logic\": false,\n",
" \"circuit_holder\": false\n",
" },\n",
" \"slots\": [\n",
" {\n",
" \"name\": \"Import\",\n",
" \"typ\": \"None\"\n",
" },\n",
" {\n",
" \"name\": \"Export\",\n",
" \"typ\": \"None\"\n",
" }\n",
" ],\n",
" \"device\": {\n",
" \"connection_list\": [\n",
" {\n",
" \"typ\": \"Chute\",\n",
" \"role\": \"Input\"\n",
" },\n",
" {\n",
" \"typ\": \"Chute\",\n",
" \"role\": \"Output\"\n",
" },\n",
" {\n",
" \"typ\": \"Data\",\n",
" \"role\": \"None\"\n",
" },\n",
" {\n",
" \"typ\": \"Power\",\n",
" \"role\": \"None\"\n",
" }\n",
" ],\n",
" \"has_activate_state\": true,\n",
" \"has_atmosphere\": false,\n",
" \"has_color_state\": false,\n",
" \"has_lock_state\": false,\n",
" \"has_mode_state\": false,\n",
" \"has_on_off_state\": true,\n",
" \"has_open_state\": true,\n",
" \"has_reagents\": false\n",
" }\n",
" },\n",
" \"111280987\": {\n",
" \"templateType\": \"ItemLogic\",\n",
" \"prefab\": {\n",
" \"prefab_name\": \"ItemTerrainManipulator\",\n",
" \"prefab_hash\": 111280987,\n",
" \"desc\": \"0.Mode0\\n1.Mode1\",\n",
" \"name\": \"Terrain Manipulator\"\n",
" },\n",
" \"item\": {\n",
" \"consumable\": false,\n",
" \"ingredient\": false,\n",
" \"max_quantity\": 1,\n",
" \"slot_class\": \"Tool\",\n",
" \"sorting_class\": \"Default\"\n",
" },\n",
" \"logic\": {\n",
" \"logic_slot_types\": {\n",
" \"0\": {\n",
" \"Occupied\": \"Read\",\n",
" \"OccupantHash\": \"Read\",\n",
" \"Quantity\": \"Read\",\n",
" \"Damage\": \"Read\",\n",
" \"Charge\": \"Read\",\n",
" \"ChargeRatio\": \"Read\",\n",
" \"Class\": \"Read\",\n",
" \"MaxQuantity\": \"Read\",\n",
" \"ReferenceId\": \"Read\"\n",
" },\n",
" \"1\": {\n",
" \"Occupied\": \"Read\",\n",
" \"OccupantHash\": \"Read\",\n",
" \"Quantity\": \"Read\",\n",
" \"Damage\": \"Read\",\n",
" \"Class\": \"Read\",\n",
" \"MaxQuantity\": \"Read\",\n",
" \"ReferenceId\": \"Read\"\n",
" }\n",
" },\n",
" \"logic_types\": {\n",
" \"Power\": \"Read\",\n",
" \"Mode\": \"ReadWrite\",\n",
" \"Error\": \"Read\",\n",
" \"Activate\": \"ReadWrite\",\n",
" \"On\": \"ReadWrite\",\n",
" \"ReferenceId\": \"Read\"\n",
" },\n",
" \"modes\": {\n",
" \"0\": \"Mode0\",\n",
" \"1\": \"Mode1\"\n",
" },\n",
" \"transmission_receiver\": false,\n",
" \"wireless_logic\": false,\n",
" \"circuit_holder\": false\n",
" },\n",
" \"slots\": [\n",
" {\n",
" \"name\": \"Battery\",\n",
" \"typ\": \"Battery\"\n",
" },\n",
" {\n",
" \"name\": \"Dirt Canister\",\n",
" \"typ\": \"Ore\"\n",
" }\n",
" ]\n",
" }\n",
"}\n",
"\"#;\n",
"use std::collections::BTreeMap;\n",
"use stationeers_data::templates::ObjectTemplate;\n",
"let parsed_db: BTreeMap<i32, ObjectTemplate> =\n",
" parse_value(&mut serde_json::Deserializer::from_str(entries))?;\n",
"println!(\"{parsed_db:?}\");"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Rust",
"language": "rust",
"name": "rust"
},
"language_info": {
"codemirror_mode": "rust",
"file_extension": ".rs",
"mimetype": "text/rust",
"name": "rust",
"pygment_lexer": "rust",
"version": ""
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -0,0 +1,23 @@
[package]
name = "stationeers_data"
version.workspace = true
edition.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
prefab_database = [] # compile with the prefab database enabled
reagent_database = [] # compile with the reagent_database enabled
tsify = ["dep:tsify", "dep:wasm-bindgen"]
wasm-bindgen = ["dep:wasm-bindgen"]
[dependencies]
const-crc32 = "1.3.0"
num-integer = "0.1.46"
phf = "0.11.2"
serde = "1.0.202"
serde_derive = "1.0.202"
serde_with = "3.8.1"
strum = { version = "0.26.2", features = ["derive", "phf", "strum_macros"] }
tsify = { version = "0.4.5", optional = true, features = ["json"] }
wasm-bindgen = { version = "0.2.92", optional = true }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,581 @@
// =================================================
// !! <-----> DO NOT MODIFY <-----> !!
//
// This module was automatically generated by an
// xtask
//
// run
//
// `cargo xtask generate -m database`
//
// from the workspace to regenerate
//
// =================================================
use crate::templates::Reagent;
pub fn build_reagent_database() -> std::collections::BTreeMap<
u8,
crate::templates::Reagent,
> {
#[allow(clippy::unreadable_literal)]
let mut map: std::collections::BTreeMap<u8, crate::templates::Reagent> = std::collections::BTreeMap::new();
map.insert(
20u8,
Reagent {
id: 20u8,
name: "Alcohol".into(),
hash: 1565803737i32,
unit: "ml".into(),
is_organic: true,
sources: vec![].into_iter().collect(),
},
);
map.insert(
36u8,
Reagent {
id: 36u8,
name: "Astroloy".into(),
hash: -1493155787i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemAstroloyIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
40u8,
Reagent {
id: 40u8,
name: "Biomass".into(),
hash: 925270362i32,
unit: "".into(),
is_organic: true,
sources: vec![("ItemBiomass".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
5u8,
Reagent {
id: 5u8,
name: "Carbon".into(),
hash: 1582746610i32,
unit: "g".into(),
is_organic: true,
sources: vec![("HumanSkull".into(), 1f64), ("ItemCharcoal".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
37u8,
Reagent {
id: 37u8,
name: "Cobalt".into(),
hash: 1702246124i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemCobaltOre".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
44u8,
Reagent {
id: 44u8,
name: "Cocoa".into(),
hash: 678781198i32,
unit: "g".into(),
is_organic: true,
sources: vec![
("ItemCocoaPowder".into(), 1f64), ("ItemCocoaTree".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
27u8,
Reagent {
id: 27u8,
name: "ColorBlue".into(),
hash: 557517660i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ReagentColorBlue".into(), 10f64)].into_iter().collect(),
},
);
map.insert(
26u8,
Reagent {
id: 26u8,
name: "ColorGreen".into(),
hash: 2129955242i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ReagentColorGreen".into(), 10f64)].into_iter().collect(),
},
);
map.insert(
29u8,
Reagent {
id: 29u8,
name: "ColorOrange".into(),
hash: 1728153015i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ReagentColorOrange".into(), 10f64)].into_iter().collect(),
},
);
map.insert(
25u8,
Reagent {
id: 25u8,
name: "ColorRed".into(),
hash: 667001276i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ReagentColorRed".into(), 10f64)].into_iter().collect(),
},
);
map.insert(
28u8,
Reagent {
id: 28u8,
name: "ColorYellow".into(),
hash: -1430202288i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ReagentColorYellow".into(), 10f64)].into_iter().collect(),
},
);
map.insert(
15u8,
Reagent {
id: 15u8,
name: "Constantan".into(),
hash: 1731241392i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemConstantanIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
7u8,
Reagent {
id: 7u8,
name: "Copper".into(),
hash: -1172078909i32,
unit: "g".into(),
is_organic: true,
sources: vec![
("ItemCopperIngot".into(), 1f64), ("ItemCopperOre".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
38u8,
Reagent {
id: 38u8,
name: "Corn".into(),
hash: 1550709753i32,
unit: "".into(),
is_organic: true,
sources: vec![("ItemCookedCorn".into(), 1f64), ("ItemCorn".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
2u8,
Reagent {
id: 2u8,
name: "Egg".into(),
hash: 1887084450i32,
unit: "".into(),
is_organic: true,
sources: vec![
("ItemCookedPowderedEggs".into(), 1f64), ("ItemEgg".into(), 1f64),
("ItemFertilizedEgg".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
13u8,
Reagent {
id: 13u8,
name: "Electrum".into(),
hash: 478264742i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemElectrumIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
24u8,
Reagent {
id: 24u8,
name: "Fenoxitone".into(),
hash: -865687737i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemFern".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
0u8,
Reagent {
id: 0u8,
name: "Flour".into(),
hash: -811006991i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemFlour".into(), 50f64)].into_iter().collect(),
},
);
map.insert(
4u8,
Reagent {
id: 4u8,
name: "Gold".into(),
hash: -409226641i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemGoldIngot".into(), 1f64), ("ItemGoldOre".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
35u8,
Reagent {
id: 35u8,
name: "Hastelloy".into(),
hash: 2019732679i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemHastelloyIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
9u8,
Reagent {
id: 9u8,
name: "Hydrocarbon".into(),
hash: 2003628602i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemCoalOre".into(), 1f64), ("ItemSolidFuel".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
34u8,
Reagent {
id: 34u8,
name: "Inconel".into(),
hash: -586072179i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemInconelIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
14u8,
Reagent {
id: 14u8,
name: "Invar".into(),
hash: -626453759i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemInvarIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
3u8,
Reagent {
id: 3u8,
name: "Iron".into(),
hash: -666742878i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemIronIngot".into(), 1f64), ("ItemIronOre".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
12u8,
Reagent {
id: 12u8,
name: "Lead".into(),
hash: -2002530571i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemLeadIngot".into(), 1f64), ("ItemLeadOre".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
1u8,
Reagent {
id: 1u8,
name: "Milk".into(),
hash: 471085864i32,
unit: "ml".into(),
is_organic: true,
sources: vec![
("ItemCookedCondensedMilk".into(), 1f64), ("ItemMilk".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
42u8,
Reagent {
id: 42u8,
name: "Mushroom".into(),
hash: 516242109i32,
unit: "g".into(),
is_organic: true,
sources: vec![
("ItemCookedMushroom".into(), 1f64), ("ItemMushroom".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
11u8,
Reagent {
id: 11u8,
name: "Nickel".into(),
hash: 556601662i32,
unit: "g".into(),
is_organic: true,
sources: vec![
("ItemNickelIngot".into(), 1f64), ("ItemNickelOre".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
21u8,
Reagent {
id: 21u8,
name: "Oil".into(),
hash: 1958538866i32,
unit: "ml".into(),
is_organic: true,
sources: vec![("ItemSoyOil".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
17u8,
Reagent {
id: 17u8,
name: "Plastic".into(),
hash: 791382247i32,
unit: "g".into(),
is_organic: true,
sources: vec![].into_iter().collect(),
},
);
map.insert(
22u8,
Reagent {
id: 22u8,
name: "Potato".into(),
hash: -1657266385i32,
unit: "".into(),
is_organic: true,
sources: vec![("ItemPotato".into(), 1f64), ("ItemPotatoBaked".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
30u8,
Reagent {
id: 30u8,
name: "Pumpkin".into(),
hash: -1250164309i32,
unit: "".into(),
is_organic: true,
sources: vec![
("ItemCookedPumpkin".into(), 1f64), ("ItemPumpkin".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
31u8,
Reagent {
id: 31u8,
name: "Rice".into(),
hash: 1951286569i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemCookedRice".into(), 1f64), ("ItemRice".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
19u8,
Reagent {
id: 19u8,
name: "SalicylicAcid".into(),
hash: -2086114347i32,
unit: "g".into(),
is_organic: true,
sources: vec![].into_iter().collect(),
},
);
map.insert(
18u8,
Reagent {
id: 18u8,
name: "Silicon".into(),
hash: -1195893171i32,
unit: "g".into(),
is_organic: true,
sources: vec![
("ItemSiliconIngot".into(), 0.1f64), ("ItemSiliconOre".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
10u8,
Reagent {
id: 10u8,
name: "Silver".into(),
hash: 687283565i32,
unit: "g".into(),
is_organic: true,
sources: vec![
("ItemSilverIngot".into(), 1f64), ("ItemSilverOre".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
16u8,
Reagent {
id: 16u8,
name: "Solder".into(),
hash: -1206542381i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemSolderIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
41u8,
Reagent {
id: 41u8,
name: "Soy".into(),
hash: 1510471435i32,
unit: "".into(),
is_organic: true,
sources: vec![
("ItemCookedSoybean".into(), 1f64), ("ItemSoybean".into(), 1f64)
]
.into_iter()
.collect(),
},
);
map.insert(
8u8,
Reagent {
id: 8u8,
name: "Steel".into(),
hash: 1331613335i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemEmptyCan".into(), 1f64), ("ItemSteelIngot".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
33u8,
Reagent {
id: 33u8,
name: "Stellite".into(),
hash: -500544800i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemStelliteIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
43u8,
Reagent {
id: 43u8,
name: "Sugar".into(),
hash: 1778746875i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemSugar".into(), 10f64), ("ItemSugarCane".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
23u8,
Reagent {
id: 23u8,
name: "Tomato".into(),
hash: 733496620i32,
unit: "".into(),
is_organic: true,
sources: vec![("ItemCookedTomato".into(), 1f64), ("ItemTomato".into(), 1f64)]
.into_iter()
.collect(),
},
);
map.insert(
6u8,
Reagent {
id: 6u8,
name: "Uranium".into(),
hash: -208860272i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemUraniumOre".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
32u8,
Reagent {
id: 32u8,
name: "Waspaloy".into(),
hash: 1787814293i32,
unit: "g".into(),
is_organic: true,
sources: vec![("ItemWaspaloyIngot".into(), 1f64)].into_iter().collect(),
},
);
map.insert(
39u8,
Reagent {
id: 39u8,
name: "Wheat".into(),
hash: -686695134i32,
unit: "".into(),
is_organic: true,
sources: vec![("ItemWheat".into(), 1f64)].into_iter().collect(),
},
);
map
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

194
stationeers_data/src/lib.rs Normal file
View File

@@ -0,0 +1,194 @@
use std::collections::BTreeMap;
pub mod templates;
pub mod enums {
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
use std::fmt::Display;
use strum::{AsRefStr, EnumIter, EnumString, FromRepr};
pub mod basic;
pub mod prefabs;
pub mod script;
#[derive(Debug)]
pub struct ParseError {
pub enm: String,
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown enum '{}'", self.enm)
}
}
impl std::error::Error for ParseError {}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumString,
)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
pub enum MemoryAccess {
Read,
Write,
ReadWrite,
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
PartialOrd,
Eq,
Ord,
Hash,
Serialize,
Deserialize,
EnumIter,
AsRefStr,
FromRepr,
EnumString,
)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
pub enum ConnectionType {
Pipe,
Power,
Data,
Chute,
Elevator,
PipeLiquid,
LandingPad,
LaunchPad,
PowerAndData,
RoboticArmRail,
#[serde(other)]
#[default]
None,
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
PartialOrd,
Eq,
Ord,
Hash,
Serialize,
Deserialize,
EnumIter,
AsRefStr,
FromRepr,
EnumString,
)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
pub enum ConnectionRole {
Input,
Input2,
Output,
Output2,
Waste,
#[serde(other)]
#[default]
None,
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
PartialOrd,
Eq,
Ord,
Hash,
Serialize,
Deserialize,
EnumIter,
AsRefStr,
FromRepr,
EnumString,
)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
#[repr(u32)]
pub enum MachineTier {
#[default]
Undefined = 0,
TierOne = 1,
TierTwo = 2,
TierThree = 3,
#[serde(other)]
Max,
}
#[derive(
Default,
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
EnumString,
AsRefStr,
EnumIter,
FromRepr,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[cfg_attr(feature = "tsify", tsify(into_wasm_abi, from_wasm_abi))]
pub enum Species {
None,
#[default]
Human,
Zrilian,
Robot,
}
}
#[must_use]
pub fn build_prefab_database() -> Option<BTreeMap<i32, templates::ObjectTemplate>> {
#[cfg(feature = "prefab_database")]
let map = Some(database::build_prefab_database());
#[cfg(not(feature = "prefab_database"))]
let map = None;
map
}
pub fn build_reagent_database() -> Option<BTreeMap<u8, templates::Reagent>> {
#[cfg(feature = "reagent_database")]
let map = Some(database::build_reagent_database());
#[cfg(not(feature = "reagent_database"))]
let map = None;
map
}
pub mod database {
#[cfg(feature = "prefab_database")]
mod prefab_map;
#[cfg(feature = "prefab_database")]
pub use prefab_map::build_prefab_database;
#[cfg(feature = "reagent_database")]
mod reagent_map;
#[cfg(feature = "reagent_database")]
pub use reagent_map::build_reagent_database;
}

View File

@@ -0,0 +1,690 @@
use std::collections::BTreeMap;
use crate::enums::{
basic::{Class, GasType, SortingClass},
script::{LogicSlotType, LogicType},
ConnectionRole, ConnectionType, MachineTier, MemoryAccess, Species,
};
use serde_with::{serde_as, DisplayFromStr};
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[cfg(feature = "tsify")]
use wasm_bindgen::prelude::*;
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
#[serde(tag = "templateType")]
pub enum ObjectTemplate {
Structure(StructureTemplate),
StructureSlots(StructureSlotsTemplate),
StructureLogic(StructureLogicTemplate),
StructureLogicDevice(StructureLogicDeviceTemplate),
StructureLogicDeviceConsumer(StructureLogicDeviceConsumerTemplate),
StructureLogicDeviceMemory(StructureLogicDeviceMemoryTemplate),
StructureLogicDeviceConsumerMemory(StructureLogicDeviceConsumerMemoryTemplate),
StructureCircuitHolder(StructureCircuitHolderTemplate),
Item(ItemTemplate),
ItemSlots(ItemSlotsTemplate),
ItemConsumer(ItemConsumerTemplate),
ItemLogic(ItemLogicTemplate),
ItemLogicMemory(ItemLogicMemoryTemplate),
ItemCircuitHolder(ItemCircuitHolderTemplate),
ItemSuit(ItemSuitTemplate),
ItemSuitLogic(ItemSuitLogicTemplate),
ItemSuitCircuitHolder(ItemSuitCircuitHolderTemplate),
Human(HumanTemplate),
}
#[allow(dead_code)]
impl ObjectTemplate {
#[allow(clippy::must_use_candidate)]
pub fn prefab(&self) -> &PrefabInfo {
#[allow(clippy::enum_glob_use)]
use ObjectTemplate::*;
match self {
Structure(s) => &s.prefab,
StructureSlots(s) => &s.prefab,
StructureLogic(s) => &s.prefab,
StructureLogicDevice(s) => &s.prefab,
StructureLogicDeviceConsumer(s) => &s.prefab,
StructureLogicDeviceMemory(s) => &s.prefab,
StructureLogicDeviceConsumerMemory(s) => &s.prefab,
StructureCircuitHolder(s) => &s.prefab,
Item(i) => &i.prefab,
ItemSlots(i) => &i.prefab,
ItemConsumer(i) => &i.prefab,
ItemLogic(i) => &i.prefab,
ItemLogicMemory(i) => &i.prefab,
ItemCircuitHolder(i) => &i.prefab,
ItemSuit(i) => &i.prefab,
ItemSuitLogic(i) => &i.prefab,
ItemSuitCircuitHolder(i) => &i.prefab,
Human(h) => &h.prefab,
}
}
}
impl From<StructureTemplate> for ObjectTemplate {
fn from(value: StructureTemplate) -> Self {
Self::Structure(value)
}
}
impl From<StructureSlotsTemplate> for ObjectTemplate {
fn from(value: StructureSlotsTemplate) -> Self {
Self::StructureSlots(value)
}
}
impl From<StructureLogicTemplate> for ObjectTemplate {
fn from(value: StructureLogicTemplate) -> Self {
Self::StructureLogic(value)
}
}
impl From<StructureLogicDeviceTemplate> for ObjectTemplate {
fn from(value: StructureLogicDeviceTemplate) -> Self {
Self::StructureLogicDevice(value)
}
}
impl From<StructureLogicDeviceConsumerTemplate> for ObjectTemplate {
fn from(value: StructureLogicDeviceConsumerTemplate) -> Self {
Self::StructureLogicDeviceConsumer(value)
}
}
impl From<StructureLogicDeviceMemoryTemplate> for ObjectTemplate {
fn from(value: StructureLogicDeviceMemoryTemplate) -> Self {
Self::StructureLogicDeviceMemory(value)
}
}
impl From<StructureLogicDeviceConsumerMemoryTemplate> for ObjectTemplate {
fn from(value: StructureLogicDeviceConsumerMemoryTemplate) -> Self {
Self::StructureLogicDeviceConsumerMemory(value)
}
}
impl From<ItemTemplate> for ObjectTemplate {
fn from(value: ItemTemplate) -> Self {
Self::Item(value)
}
}
impl From<ItemSlotsTemplate> for ObjectTemplate {
fn from(value: ItemSlotsTemplate) -> Self {
Self::ItemSlots(value)
}
}
impl From<ItemConsumerTemplate> for ObjectTemplate {
fn from(value: ItemConsumerTemplate) -> Self {
Self::ItemConsumer(value)
}
}
impl From<ItemLogicTemplate> for ObjectTemplate {
fn from(value: ItemLogicTemplate) -> Self {
Self::ItemLogic(value)
}
}
impl From<ItemLogicMemoryTemplate> for ObjectTemplate {
fn from(value: ItemLogicMemoryTemplate) -> Self {
Self::ItemLogicMemory(value)
}
}
impl From<ItemSuitCircuitHolderTemplate> for ObjectTemplate {
fn from(value: ItemSuitCircuitHolderTemplate) -> Self {
Self::ItemSuitCircuitHolder(value)
}
}
impl From<ItemSuitTemplate> for ObjectTemplate {
fn from(value: ItemSuitTemplate) -> Self {
Self::ItemSuit(value)
}
}
impl From<ItemSuitLogicTemplate> for ObjectTemplate {
fn from(value: ItemSuitLogicTemplate) -> Self {
Self::ItemSuitLogic(value)
}
}
impl From<ItemCircuitHolderTemplate> for ObjectTemplate {
fn from(value: ItemCircuitHolderTemplate) -> Self {
Self::ItemCircuitHolder(value)
}
}
impl From<StructureCircuitHolderTemplate> for ObjectTemplate {
fn from(value: StructureCircuitHolderTemplate) -> Self {
Self::StructureCircuitHolder(value)
}
}
impl From<HumanTemplate> for ObjectTemplate {
fn from(value: HumanTemplate) -> Self {
Self::Human(value)
}
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct HumanTemplate {
pub prefab: PrefabInfo,
pub species: Species,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct PrefabInfo {
pub prefab_name: String,
pub prefab_hash: i32,
pub desc: String,
pub name: String,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum SlotInfo {
Direct {
name: String,
class: Class,
index: u32,
},
Proxy {
name: String,
index: u32,
},
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct LogicInfo {
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(
feature = "tsify",
tsify(type = "Map<string, Map<LogicSlotType, MemoryAccess>>")
)]
pub logic_slot_types: BTreeMap<u32, BTreeMap<LogicSlotType, MemoryAccess>>,
pub logic_types: BTreeMap<LogicType, MemoryAccess>,
#[serde_as(as = "Option<BTreeMap<DisplayFromStr, _>>")]
#[cfg_attr(feature = "tsify", tsify(optional, type = "Map<string, string>"))]
pub modes: Option<BTreeMap<u32, String>>,
pub transmission_receiver: bool,
pub wireless_logic: bool,
pub circuit_holder: bool,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemInfo {
pub consumable: bool,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub filter_type: Option<GasType>,
pub ingredient: bool,
pub max_quantity: u32,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub reagents: Option<BTreeMap<String, f64>>,
pub slot_class: Class,
pub sorting_class: SortingClass,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ConnectionInfo {
pub typ: ConnectionType,
pub role: ConnectionRole,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct DeviceInfo {
pub connection_list: Vec<ConnectionInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub device_pins_length: Option<u32>,
pub has_activate_state: bool,
pub has_atmosphere: bool,
pub has_color_state: bool,
pub has_lock_state: bool,
pub has_mode_state: bool,
pub has_on_off_state: bool,
pub has_open_state: bool,
pub has_reagents: bool,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ConsumerInfo {
pub consumed_resources: Vec<String>,
pub processed_reagents: Vec<i32>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Reagent {
pub id: u8,
pub name: String,
pub hash: i32,
pub unit: String,
pub is_organic: bool,
pub sources: BTreeMap<String, f64>,
}
impl Reagent {
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct RecipeRange {
pub start: f64,
pub stop: f64,
pub is_valid: bool,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct RecipeGasMix {
pub rule: i64,
pub is_any: bool,
pub is_any_to_remove: bool,
pub reagents: BTreeMap<String, f64>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Recipe {
pub target_prefab: String,
pub target_prefab_hash: i32,
pub tier: MachineTier,
pub time: f64,
pub energy: f64,
pub temperature: RecipeRange,
pub pressure: RecipeRange,
pub required_mix: RecipeGasMix,
pub count_types: i64,
pub reagents: BTreeMap<String, f64>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct RecipeOrder {
pub recipe: Recipe,
pub quantity: u32,
}
impl Recipe {
pub fn with_target(mut self, prefab: impl Into<String>) -> Self {
let prefab: String = prefab.into();
self.target_prefab_hash = const_crc32::crc32(prefab.as_bytes()) as i32;
self.target_prefab = prefab;
self
}
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct FabricatorInfo {
pub tier: MachineTier,
pub recipes: Vec<Recipe>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureInfo {
pub small_grid: bool,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum InstructionPartType {
Bool8,
Byte8,
Int32,
UInt32,
Short16,
UShort16,
Unused(u32),
Unknown(String),
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct InstructionPart {
pub range: (u32, u32),
pub name: String,
pub typ: InstructionPartType,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Instruction {
pub description: String,
pub description_stripped: String,
pub typ: String,
pub value: i64,
pub valid: (u32, Option<u32>),
pub parts: Vec<InstructionPart>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct MemoryInfo {
#[cfg_attr(feature = "tsify", tsify(optional))]
pub instructions: Option<BTreeMap<String, Instruction>>,
pub memory_access: MemoryAccess,
pub memory_size: u32,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ThermalInfo {
pub convection_factor: f32,
pub radiation_factor: f32,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct InternalAtmoInfo {
pub volume: f32,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct SuitInfo {
pub hygiene_reduction_multiplier: f32,
pub waste_max_pressure: f32,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureSlotsTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureLogicTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureLogicDeviceTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub device: DeviceInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureLogicDeviceConsumerTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub device: DeviceInfo,
pub consumer_info: ConsumerInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub fabricator_info: Option<FabricatorInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureLogicDeviceMemoryTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub device: DeviceInfo,
pub memory: MemoryInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureCircuitHolderTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub device: DeviceInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct StructureLogicDeviceConsumerMemoryTemplate {
pub prefab: PrefabInfo,
pub structure: StructureInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub device: DeviceInfo,
pub consumer_info: ConsumerInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub fabricator_info: Option<FabricatorInfo>,
pub memory: MemoryInfo,
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemSlotsTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemConsumerTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub consumer_info: ConsumerInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemLogicTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemLogicMemoryTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub memory: MemoryInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemCircuitHolderTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemSuitTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub suit_info: SuitInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemSuitLogicTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub suit_info: SuitInfo,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct ItemSuitCircuitHolderTemplate {
pub prefab: PrefabInfo,
pub item: ItemInfo,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub thermal_info: Option<ThermalInfo>,
#[cfg_attr(feature = "tsify", tsify(optional))]
pub internal_atmo_info: Option<InternalAtmoInfo>,
pub logic: LogicInfo,
#[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
#[cfg_attr(feature = "tsify", tsify(type = "Map<string, SlotInfo>"))]
pub slots: BTreeMap<u32, SlotInfo>,
pub suit_info: SuitInfo,
pub memory: MemoryInfo,
}

View File

@@ -15,18 +15,20 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib widget\n",
"import json\n",
"\n",
"database = {}\n",
"\n",
"with open(\"data/database.json\", \"r\") as f:\n",
" database = json.load(f)\n",
"with open(\"src/ts/virtualMachine/prefabDatabase.ts\", \"r\") as f:\n",
" contents = f.read().removeprefix(\"export default \").removesuffix(\" as const\")\n",
" database = json.loads(contents)\n",
"\n",
"db = database[\"db\"]"
"db = database[\"prefabs\"]"
]
},
{
@@ -38,7 +40,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -58,7 +60,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -80,7 +82,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 35,
"metadata": {},
"outputs": [],
"source": [
@@ -105,9 +107,9 @@
" better_matches = []\n",
" for can in filtered_matches:\n",
" name, match, mapping = can\n",
" if mapping.startswith(\"Item\") and mapping in name:\n",
" if mapping.startswith(\"Item\") and mapping in name or mapping.lower() in name:\n",
" better_matches.append((name, match, mapping))\n",
" elif mapping.startswith(\"Structure\") and mapping in name:\n",
" elif mapping.startswith(\"Structure\") and mapping in name or mapping.lower() in name:\n",
" better_matches.append((name, match, mapping))\n",
" if len(better_matches) > 0:\n",
" filtered_matches = better_matches\n",
@@ -127,7 +129,7 @@
" direct = []\n",
" for can in filtered_matches:\n",
" name, match, mapping = can\n",
" if f\"{match}-\" in name:\n",
" if f\"{match}-\" in name or f\"{match.lower()}-\" in name:\n",
" direct.append((name, match, mapping))\n",
" if len(direct) > 0:\n",
" filtered_matches = direct\n",
@@ -154,7 +156,7 @@
" continue\n",
" elif name.startswith(\"Kit\") and not mapping.startswith(\"Kit\"):\n",
" continue\n",
" elif not name.startswith(match):\n",
" elif not (name.startswith(match) or name.startswith(match.lower())):\n",
" continue\n",
" not_worse.append((name, match, mapping))\n",
" if len(not_worse) > 0:\n",
@@ -171,14 +173,15 @@
"\n",
"for entry in db.values():\n",
" candidates = []\n",
" entry_name = entry[\"prefab\"][\"prefab_name\"]\n",
" for name in names:\n",
" if entry[\"name\"] in name:\n",
" candidates.append((name, entry[\"name\"], entry[\"name\"]))\n",
" if entry[\"name\"].removeprefix(\"Item\") in name:\n",
" candidates.append((name, entry[\"name\"].removeprefix(\"Item\"), entry[\"name\"]))\n",
" if entry[\"name\"].removeprefix(\"Structure\") in name:\n",
" candidates.append((name, entry[\"name\"].removeprefix(\"Structure\"), entry[\"name\"]))\n",
" image_candidates[entry[\"name\"]] = filter_candidates(candidates)"
" if entry_name in name or entry_name.lower() in name:\n",
" candidates.append((name, entry_name, entry_name))\n",
" if entry_name.removeprefix(\"Item\") in name or entry_name.removeprefix(\"Item\").lower() in name:\n",
" candidates.append((name, entry_name.removeprefix(\"Item\"), entry_name))\n",
" if entry_name.removeprefix(\"Structure\") in name or entry_name.removeprefix(\"Structure\").lower() in name:\n",
" candidates.append((name, entry_name.removeprefix(\"Structure\"), entry_name))\n",
" image_candidates[entry_name] = filter_candidates(candidates)"
]
},
{
@@ -190,7 +193,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
@@ -206,12 +209,12 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Prepare out List of file copies. at this point a few items will never have a match. and one or two will have two choices but those choices will be arbitrary."
"Prepare out List of file copies. at this point a few items will never have a match. and one or two will have two choices but those choices will be filtered again by passing them through some extra function."
]
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 40,
"metadata": {},
"outputs": [
{
@@ -227,25 +230,87 @@
"ItemHorticultureBelt []\n",
"ItemKitLiquidRegulator []\n",
"ItemKitPortablesConnector []\n",
"ItemMushroom ['ItemMushroom-resources.assets-3022.png', 'ItemMushroom-resources.assets-9304.png']\n",
"ItemPlantEndothermic_Creative []\n",
"ItemPlantThermogenic_Creative []\n",
"ItemSuitModCryogenicUpgrade []\n",
"Landingpad_GasConnectorInwardPiece []\n",
"Landingpad_LiquidConnectorInwardPiece []\n",
"StructureBlocker []\n",
"StructureElevatorLevelIndustrial []\n",
"StructureLogicSorter []\n",
"StructurePlinth []\n"
]
}
],
"source": [
"%matplotlib widget\n",
"to_copy = []\n",
"\n",
"from IPython.display import Image, display\n",
"\n",
"gases = [(\"Oxygen\", \"White\"), (\"Nitrogen\", \"Black\"), (\"CarbonDioxide\", \"Yellow\"), (\"Fuel\", \"Orange\")]\n",
"\n",
"colors = [\"White\", \"Black\", \"Gray\", \"Khaki\", \"Brown\", \"Orange\", \"Yellow\", \"Red\", \"Green\", \"Blue\", \"Purple\"]\n",
"\n",
"def split_gas(name):\n",
" for gas, color in gases:\n",
" if name.endswith(gas):\n",
" return (name.removesuffix(gas), gas, color)\n",
" elif name.lower().endswith(gas):\n",
" return (name.lower().removesuffix(gas), gas, color)\n",
" elif name.lower().endswith(gas.lower()):\n",
" return (name.lower().removesuffix(gas.lower()), gas.lower(), color.lower())\n",
" return [name, None, None]\n",
"\n",
"def match_gas_color(name, candidates):\n",
" mat, gas, color = split_gas(name)\n",
" seek = f\"{mat}_{color}\"\n",
" if gas is not None:\n",
" for candidate in candidates:\n",
" if seek in candidate or seek.lower() in candidate:\n",
" return candidate\n",
" return None\n",
"\n",
"def prefer_direct(name, candidates):\n",
" for candidate in candidates:\n",
" if f\"{name}-\" in candidate or f\"{name.lower()}-\" in candidate:\n",
" return candidate\n",
" return None\n",
"\n",
"def prefer_uncolored(name, candidates):\n",
" for candidate in candidates:\n",
" for color in colors:\n",
" if f\"_{color}\" not in candidate and f\"_{color.lower()}\" not in candidate:\n",
" return candidate\n",
" return None\n",
"\n",
"def prefer_lower_match(name, candidates):\n",
" for candidate in candidates:\n",
" if f\"{name.lower()}-\" in candidate:\n",
" return candidate\n",
" return None\n",
"\n",
"filter_funcs = [prefer_lower_match, prefer_direct, match_gas_color, prefer_uncolored]\n",
"\n",
"for name, candidates in image_candidates.items():\n",
" if len(candidates) != 1:\n",
" found = False\n",
" for func in filter_funcs:\n",
" candidate = func(name, candidates)\n",
" if candidate is not None:\n",
" to_copy.append((name, candidate))\n",
" found = True\n",
" if found:\n",
" continue\n",
" print(name, candidates)\n",
" if len(candidates) > 1:\n",
" for candidate in candidates:\n",
" print(candidate)\n",
" display(Image(datapath / candidate))\n",
" \n",
" #take first as fallback\n",
" to_copy.append((name, candidates[0]))\n",
" # to_copy.append((name, candidates[0]))\n",
" raise StopExecution\n",
" else:\n",
" # print(name, candidates)\n",
" to_copy.append((name, candidates[0]))\n"
@@ -253,7 +318,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 41,
"metadata": {},
"outputs": [
{
@@ -334,14 +399,28 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 42,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "3497a8d33d6a45879586d4c7441e3f60",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"IntProgress(value=0, max=1288)"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"1266 of 1266 | 100.00% \n",
"1288 of 1288 | 100.00% \n",
"Done\n"
]
}
@@ -352,6 +431,12 @@
"destpath = Path(\"img/stationpedia\")\n",
"total_files = len(to_copy)\n",
"\n",
"from ipywidgets import IntProgress\n",
"from IPython.display import display\n",
"import time\n",
"\n",
"f = IntProgress(min=0, max=total_files)\n",
"display(f)\n",
"count = 0\n",
"print ( f\"{count} of {total_files} | { count / total_files * 100}\", end=\"\\r\")\n",
"for name, file in to_copy:\n",
@@ -359,6 +444,7 @@
" dest = destpath / f\"{name}.png\"\n",
" shutil.copy(source, dest)\n",
" count += 1\n",
" f.value = count\n",
" print ( f\"{count} of {total_files} | { (count / total_files) * 100 :.2f}% \", end=\"\\r\")\n",
"print()\n",
"print(\"Done\")\n",
@@ -382,7 +468,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
"version": "3.12.5"
}
},
"nbformat": 4,

View File

@@ -52,23 +52,37 @@
"brnaz",
"brne",
"brnez",
"bson",
"Circuitboard",
"clazz",
"codegen",
"Comlink",
"datapoints",
"dbutils",
"Depressurising",
"deviceslength",
"dodgerblue",
"dont",
"endpos",
"getd",
"Hardsuit",
"hardwrap",
"hashables",
"hstack",
"IDBP",
"idxs",
"infile",
"jetpack",
"Keybind",
"labelledby",
"lawngreen",
"lbns",
"leeoniya",
"lightdom",
"logicable",
"LogicSlotType",
"logicslottypes",
"LogicSlotTypes",
"logictype",
"logictypes",
"lparen",
@@ -82,6 +96,7 @@
"pedia",
"pinf",
"popperjs",
"preact",
"preproc",
"Pressurising",
"putd",
@@ -91,20 +106,22 @@
"regen",
"rocketstation",
"rparen",
"rsbuild",
"rspack",
"sapz",
"sattellite",
"sdns",
"sdse",
"searchbtn",
"seqz",
"serde",
"sgez",
"sgtz",
"slez",
"Slotable",
"slotclass",
"slotlogic",
"slotlogicable",
"slotlogictype",
"slotlogictypes",
"slottype",
"sltz",
"snan",
@@ -117,10 +134,17 @@
"stationpedia",
"tablist",
"tabpanel",
"tailwindcss",
"themelist",
"tokentype",
"trunc",
"whos"
"ufuzzy",
"VMIC",
"VMUI",
"vstack",
"whitesmoke",
"whos",
"wicg"
],
"flagWords": [],
"language": "en",

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Some files were not shown because too many files have changed in this diff Show More