create first preset parameters

This commit is contained in:
Thomas Weinhold 2025-12-16 23:16:11 +01:00
commit 7389a0d732
9 changed files with 456 additions and 17 deletions

60
doc/LapSymbol.svg Normal file
View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="2.0881214mm"
height="3.2305562mm"
viewBox="0 0 2.0881214 3.2305562"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (unknown)"
sodipodi:docname="LapSymbol.svg"
inkscape:export-filename="LapSymbol_export.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
inkscape:zoom="32.640853"
inkscape:cx="10.860623"
inkscape:cy="6.9851116"
inkscape:window-width="2477"
inkscape:window-height="1217"
inkscape:window-x="1215"
inkscape:window-y="1185"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<linearGradient
id="swatch1"
inkscape:swatch="solid">
<stop
style="stop-color:#48de18;stop-opacity:1;"
offset="0"
id="stop1" />
</linearGradient>
</defs>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-37.829407,-128.22568)">
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-opacity:1"
d="m 38.259322,128.24181 -0.280858,2.28695 c 0,0 0.582809,0.0153 1.41574,0.0287 0.349473,0.006 0.844,-0.9939 -0.452807,-0.94575 -0.201107,1.58587 -0.240732,1.8227 -0.240732,1.8227"
id="path2"
sodipodi:nodetypes="ccscc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -65,6 +65,9 @@ div.NdpcParameterLabelAndInput > div:first-child {
div.NdpcSubContainer {
padding-top: 1.0em;
padding-left: 0.5em;
display: flex;
flex-wrap: wrap;
gap: 1em;
}
div.NdpcSubContainer > div {

View file

@ -1,26 +1,317 @@
use sslo_lib::ndpc::collection::Collection;
use sslo_lib::ndpc::param_enum_single::ParamEnumSingle;
use sslo_lib::ndpc::param_i64::ParamI64;
use sslo_lib::ndpc::param_str::ParamStr;
use sslo_lib::ndpc::parameter::Parameter;
use sslo_lib::parameters::{SupportedSim, HTML_LAP_SYMBOL};
use crate::user_grade::License;
macro_rules! make_car_class {
($parent_pc:expr, $($n:expr),*) => {
$(
let mut sub_pc = Collection::new(concat!("Car Class ", $n).to_string());
// car class
let mut p = ParamEnumSingle::new(concat!("CarClass", $n, "Class"));
p.add_enum_item("none", "None");
p.meta_mut().label = "Car Class".to_string();
p.meta_mut().description = Some("Select here a car class to drive".to_string());
sub_pc.add_parameter(p);
// license min
let mut p = ParamEnumSingle::new(concat!("CarClass", $n, "LicenseMin"));
for lic in License::list() {
p.add_enum_item(lic.title_str(), lic.title_str());
}
p.meta_mut().label = "Minim License".to_string();
p.meta_mut().description = Some("This defines the minimum license level for drivers to use drive class".to_string());
sub_pc.add_parameter(p);
// license max
let mut p = ParamEnumSingle::new(concat!("CarClass", $n, "LicenseMax"));
for lic in License::list() {
p.add_enum_item(lic.title_str(), lic.title_str());
let ret = p.set_value_str_safe(lic.title_str().to_string()); // setting value to max license
debug_assert!(ret.is_ok());
}
p.meta_mut().label = "Maximum License".to_string();
p.meta_mut().description = Some("This defines the maximum license level of drivers to use drive class".to_string());
sub_pc.add_parameter(p);
// registration
let mut p = ParamEnumSingle::new(concat!("CarClass", $n, "Registration"));
p.add_enum_item("NoReg", "No Registration");
p.add_enum_item("RegTime", "Registration: first come, first served");
p.add_enum_item("RegQuali", "Registration: qualify in free practice");
p.meta_mut().label = "Registration".to_string();
p.meta_mut().description = Some("No registration means that drivers cannot register at all (only spontaneous joining\nRegistration allows drivers to occupy seats (and preserve skins)\nIf more drivers register than pits are available, it is possible to use the free practice as qualification".to_string());
sub_pc.add_parameter(p);
// free cars
let mut p = ParamEnumSingle::new(concat!("CarClass", $n, "FreeCars"));
p.add_enum_item("yes", "Yes");
p.add_enum_item("no", "No");
p.meta_mut().label = "Free Cars".to_string();
p.meta_mut().description = Some("If activated, the server will provide free, unoccupied cars in this class for spontaneously joining drivers".to_string());
sub_pc.add_parameter(p);
$parent_pc.add_collection(sub_pc);
)*
};
}
macro_rules! make_weather {
($parent_pc:expr, $($n:expr),*) => {
$(
let mut pc = Collection::new(concat!("Weather ", $n).to_string());
// mode
let mut p = ParamEnumSingle::new(concat!("Weather", $n, "Mode"));
p.add_enum_item("Custom", "Custom");
p.add_enum_item("RealLive", "Real, Live");
p.add_enum_item("Real-3", "Real, -3 Month");
p.add_enum_item("Real-6", "Real, -6 Month");
p.add_enum_item("Real-9", "Real, -9 Month");
p.meta_mut().label = "Mode".to_string();
p.meta_mut().description = Some("Select if you want to custom adjust or use real weather settings".to_string());
pc.add_parameter(p);
// environment
let mut p = ParamEnumSingle::new(concat!("Weather", $n, "Environment"));
p.add_enum_item("clear", "Clear");
p.add_enum_item("broken", "Broken Clouds");
p.add_enum_item("cloudy", "Cloudy");
p.add_enum_item("mist", "Mist");
p.add_enum_item("fog", "Fog");
p.meta_mut().label = "Environment".to_string();
p.meta_mut().description = Some("Select the basic weather environment (skybox)".to_string());
pc.add_parameter(p);
// precipitation
let mut p = ParamEnumSingle::new(concat!("Weather", $n, "Precipitation"));
p.add_enum_item("none", "None");
p.add_enum_item("drizzle", "Drizzle");
p.add_enum_item("light", "Light");
p.add_enum_item("medium", "Medium");
p.add_enum_item("heavy", "Heavy");
p.meta_mut().label = "Precipitation".to_string();
p.meta_mut().description = Some("Select the amount of rain".to_string());
pc.add_parameter(p);
// puddles
let mut p = ParamEnumSingle::new(concat!("Weather", $n, "Puddles"));
p.add_enum_item("none", "None");
p.add_enum_item("few", "Few");
p.add_enum_item("some", "Some");
p.add_enum_item("many", "Many");
p.add_enum_item("ocean", "Ocean");
p.meta_mut().label = "Puddles".to_string();
p.meta_mut().description = Some("Adjust the amount of wet puddles on the track".to_string());
pc.add_parameter(p);
// temperature ambient
let mut p = ParamI64::new(concat!("Weather", $n, "Ambient"), 22, 0, 50);
p.meta_mut().label = "Ambient".to_string();
p.meta_mut().description = Some("The ambient temperature of the environment".to_string());
p.meta_mut().unit = Some("°C".to_string());
pc.add_parameter(p);
// temperature road
let mut p = ParamI64::new(concat!("Weather", $n, "Road"), 22, 0, 50);
p.meta_mut().label = "Road".to_string();
p.meta_mut().description = Some("The temperature of the road/tarmac".to_string());
p.meta_mut().unit = Some("°C".to_string());
pc.add_parameter(p);
// wind speed
let mut p = ParamEnumSingle::new(concat!("Weather", $n, "WindSpeed"));
p.add_enum_item("none", "None");
p.add_enum_item("light", "light");
p.add_enum_item("medium", "Medium");
p.add_enum_item("heavy", "Heavy");
p.meta_mut().label = "Wind Speed".to_string();
p.meta_mut().description = Some("Wind can affect breaking points".to_string());
pc.add_parameter(p);
// wind direction
let mut p = ParamEnumSingle::new(concat!("Weather", $n, "WindDirection"));
p.add_enum_item("N", "North");
p.add_enum_item("NE", "North-East");
p.add_enum_item("E", "East");
p.add_enum_item("SE", "South-East");
p.add_enum_item("S", "South");
p.add_enum_item("SW", "South-West");
p.add_enum_item("W", "West");
p.add_enum_item("NW", "North-West");
p.meta_mut().label = "Wind Direction".to_string();
p.meta_mut().description = Some("Wind can affect breaking points".to_string());
pc.add_parameter(p);
$parent_pc.add_collection(pc);
)*
};
}
/// Returns a template for a Preset parameter collection
pub fn get() -> Collection {
// top level collection
let mut pc_top = Collection::new("Preset".to_string());
let pc = get_general();
pc_top.add_collection(pc);
return pc_top;
let mut pc = Collection::new("Preset".to_string());
pc.add_collection(get_general());
pc.add_collection(get_session());
pc.add_collection(get_server());
pc.add_collection(get_car_classes());
pc.add_collection(get_weather());
return pc;
}
fn get_car_classes() -> Collection {
let mut pc = Collection::new("Car Classes".to_string());
make_car_class!(&mut pc,1,2,3,4);
return pc;
}
fn get_weather() -> Collection {
let mut pc = Collection::new("Weather".to_string());
// amount of weather per server run
let mut p = ParamEnumSingle::new("WeatherCount");
p.add_enum_item("1", "Static Weather");
p.add_enum_item("2", "Two Weather Sets");
p.add_enum_item("3", "Three Weather Sets");
p.add_enum_item("4", "Four Weather Sets");
p.meta_mut().label = "Weathr Changes".to_string();
p.meta_mut().description = Some("Defines the amount of weather settings that change during a server run (not per session)".to_string());
pc.add_parameter(p);
make_weather!(&mut pc,1,2,3,4);
return pc;
}
fn get_general() -> Collection {
let mut pc = Collection::new("General".to_string());
let mut p = ParamStr::new("ServerName", "SsloLeagueServer");
p.meta_mut().label = "Server Name".to_string();
// supported sim
let mut p = ParamEnumSingle::new("Sim");
for sim in SupportedSim::list() {
p.add_enum_item(sim.to_string_short(), sim.to_string());
}
p.meta_mut().label = "Simulator".to_string();
p.meta_mut().description = Some("The simulator that shall be used (select None if not decided here)".to_string());
pc.add_parameter(p);
// driving aids
let mut p = ParamEnumSingle::new("Aids");
p.add_enum_item("amateur", "Amateur");
p.add_enum_item("intermediate", "Intermediate");
p.add_enum_item("pro", "Professional");
p.add_enum_item("hardcore", "Hardcore");
p.meta_mut().label = "Driving Aids".to_string();
p.meta_mut().description = Some("The higher the level, the less aids like racing line or traction control are allowed.\nAmateur activates all aids, while hardcore forces realism\nThis also affects the speed limit in the pits.".to_string());
pc.add_parameter(p);
// damage
let mut p = ParamEnumSingle::new("Damage");
p.add_enum_item("none", "None");
p.add_enum_item("light", "Light");
p.add_enum_item("medium", "Medium");
p.add_enum_item("Full", "Full");
p.meta_mut().label = "Damage".to_string();
p.meta_mut().description = Some("The level of physical damage on the cars".to_string());
pc.add_parameter(p);
// penalties
let mut p = ParamEnumSingle::new("Penalties");
p.add_enum_item("none", "None");
p.add_enum_item("basic", "Basic");
p.add_enum_item("moderate", "Moderate");
p.add_enum_item("all", "All");
p.meta_mut().label = "Penalties".to_string();
p.meta_mut().description = Some("The level of in-game penalties (speeding, crossing, track-limits, etc.)".to_string());
pc.add_parameter(p);
// scoring
let mut p = ParamStr::new("Scoring", "25; 18; 15; 12; 10; 8; 6; 4; 2; 1");
p.meta_mut().unit = Some("pts".to_string());
p.meta_mut().label = "Scoring Points".to_string();
p.meta_mut().description = Some("Defines the scoring points, applied to race results (important for race series)\nA semicolon separated list of points (beginning with first place points)".to_string());
pc.add_parameter(p);
return pc
}
fn get_session() -> Collection {
let mut pc = Collection::new("Session".to_string());
// practice duration
let mut p = ParamI64::new("PracticeDuration", 60, 0, 24*60);
p.meta_mut().unit = Some("min".to_string());
p.meta_mut().label = "Free Practice Duration".to_string();
p.meta_mut().description = Some("The duration of a practice session, before the session restarts or ends (in Minutes)".to_string());
pc.add_parameter(p);
// practice opening
let mut p = ParamI64::new("PracticeOpening", 7*24, 0, 365*24);
p.meta_mut().unit = Some("h".to_string());
p.meta_mut().label = "Free Practice Opening".to_string();
p.meta_mut().description = Some("The time before the warmup session, when the free practice server shall start (in hours)".to_string());
pc.add_parameter(p);
// warmup duration
let mut p = ParamI64::new("WarmupDuration", 30, 0, 24*60);
p.meta_mut().unit = Some("min".to_string());
p.meta_mut().label = "Warmup Duration".to_string();
p.meta_mut().description = Some("The duration of the warmup, before the race session (in Minutes)".to_string());
pc.add_parameter(p);
// qualifying duration
let mut p = ParamI64::new("QualifyingDuration", 15, 0, 24*60);
p.meta_mut().unit = Some("min".to_string());
p.meta_mut().label = "Qualifying Duration".to_string();
p.meta_mut().description = Some("The duration of the qualifying session, which is driven directly before the race (if the Sim supports this)".to_string());
pc.add_parameter(p);
// race mode
let mut p = ParamEnumSingle::new("RaceMode");
p.add_enum_item("time", "Time Limit");
p.add_enum_item("laps", "Lap Limit");
p.meta_mut().label = "Race Mode".to_string();
p.meta_mut().description = Some("Define if the race shall be ended after a duration of time, or after an amount of laps".to_string());
pc.add_parameter(p);
// race duration time
let mut p = ParamI64::new("RaceDurationTime", 30, 0, 24*60);
p.meta_mut().unit = Some("min".to_string());
p.meta_mut().label = "Race Duration".to_string();
p.meta_mut().description = Some("The duration of the race in Minutes".to_string());
pc.add_parameter(p);
// race duration laps
let mut p = ParamI64::new("RaceDurationLaps", 15, 0, 9999);
p.meta_mut().unit = Some(HTML_LAP_SYMBOL.to_string());
p.meta_mut().label = "Race Duration".to_string();
p.meta_mut().description = Some("The duration of the race in Laps".to_string());
pc.add_parameter(p);
return pc;
}
fn get_server() -> Collection {
let mut pc = Collection::new("Server".to_string());
// server name
let mut p = ParamStr::new("ServerName", "SSLO League Server");
p.meta_mut().label = "Server Name".to_string();
p.meta_mut().description = Some("An arbitrary name of the server".to_string());
pc.add_parameter(p);
// client password
let mut p = ParamStr::new("ClientPassword", "");
p.meta_mut().label = "Client Password".to_string();
p.meta_mut().description = Some("Define here a password that drivers need for joining the server".to_string());
pc.add_parameter(p);
return pc;
}

View file

@ -17,6 +17,10 @@ pub enum License {
impl License {
pub fn list() -> Vec<Self> {
vec![Self::Green, Self::Bronze, Self::Silver, Self::Gold, Self::Platinum, Self::Alien]
}
/// The string portion that is used to compose the user title
pub fn title_str(&self) -> &'static str {
match self {

View file

@ -4,3 +4,4 @@ pub mod collection;
pub mod parameter;
pub mod param_i64;
pub mod param_str;
pub mod param_enum_single;

View file

@ -16,7 +16,7 @@ impl Collection {
}}
pub fn add_parameter(&mut self, parameter: Box<dyn Parameter>) {
debug_assert!(self.find_parameter(parameter.meta().upid()).is_none());
debug_assert!(self.find_parameter(parameter.meta().upid()).is_none()); // check if parameter upid already exists
self.child_parameters.push(parameter);
}

View file

@ -0,0 +1,69 @@
use crate::ndpc::parameter::{Parameter, ParameterMetaData};
use crate::error::SsloError;
pub struct ParamEnumItem {
key: &'static str,
label: &'static str,
}
pub struct ParamEnumSingle {
meta: ParameterMetaData,
enumitems: Vec<ParamEnumItem>,
value: String,
}
impl ParamEnumSingle {
pub fn new(upid: &'static str) -> Box<Self> {
Box::new(Self {
meta: ParameterMetaData::new(upid),
enumitems: Vec::new(),
value: "".to_string(),
})
}
pub fn add_enum_item(&mut self, key: &'static str, label: &'static str) {
if self.enumitems.len() == 0 {
self.value = key.to_string();
}
self.enumitems.push(ParamEnumItem { key, label });
}
pub fn iter_enums(&self) -> impl Iterator<Item = &ParamEnumItem> {
self.enumitems.iter()
}
}
impl Parameter for ParamEnumSingle {
fn meta(&self) -> &ParameterMetaData {&self.meta}
fn meta_mut(&mut self) -> &mut ParameterMetaData {&mut self.meta}
fn html_input(&self) -> String {
let options: Vec<String> = self.enumitems
.iter().map(|item| {
let selected = match self.value == item.key {true => " selected=\"true\"", false => ""};
format!("<option value=\"{}\"{}/>{}</option>",
item.key,
selected,
item.label)
}).collect();
format!("<select>{}</select>", options.join(""))
}
fn get_value_str_unsafe(&self) -> String {
self.value.clone()
}
fn set_value_str_unsafe(&mut self, value: String) -> Result<(), SsloError> {
for ei in &self.enumitems {
if ei.key == value.as_str() {
self.value = ei.key.to_string();
return Ok(())
}
}
Err(SsloError::CdsFromValueStrError(value.to_string()))
}
}

View file

@ -24,7 +24,7 @@ impl Parameter for ParamI64 {
fn meta_mut(&mut self) -> &mut ParameterMetaData {&mut self.meta}
fn html_input(&self) -> String {
format!("<input type=\"number\" value=\"{}\" min=\"{}\" max)\"{}\"/>",
format!("<input type=\"number\" value=\"{}\" min=\"{}\" max=\"{}\"/>",
self.value, self.min, self.max,
)
}

View file

@ -16,6 +16,8 @@ pub const LEAGUE_REPORT_OFFLINE_TOLERANCE: i64 = 60*5;
/// The maximum dimension of the league logo
pub const LEAGUE_LOGO_MAX_DIMENSION: ImageDimension = ImageDimension{width: 800, height: 800};
pub const HTML_LAP_SYMBOL: &str = "<svg viewBox=\"0 0 2.0881214 3.2305562\"><g transform=\"translate(-37.829407,-128.22568)\"><path style=\"fill:none;stroke:currentColor;stroke-width:0.1;\" d=\"m 38.259322,128.24181 -0.280858,2.28695 c 0,0 0.582809,0.0153 1.41574,0.0287 0.349473,0.006 0.844,-0.9939 -0.452807,-0.94575 -0.201107,1.58587 -0.240732,1.8227 -0.240732,1.8227\"/></g></svg>";
pub struct ImageDimension {
pub width: u32,
@ -48,7 +50,16 @@ impl SupportedSim {
SupportedSim::from_u32(i).unwrap_or_else(|| SupportedSim::NoSim)
}
pub fn to_string(&self) -> String {
pub fn list() -> Vec<Self> {
vec![Self::NoSim,
Self::AssettoCorsa, Self::AssettoCorsaCompetizione, Self::AssettoCorsaEvo,
Self::Automobilista, Self::Automobilista2,
Self::Rennsport,
Self::LeMansUltimate,
]
}
pub fn to_string(&self) -> &'static str {
match self {
SupportedSim::NoSim => "None",
SupportedSim::AssettoCorsa => "Assetto Corsa",
@ -58,10 +69,10 @@ impl SupportedSim {
SupportedSim::Automobilista2 => "Automobilista 2",
SupportedSim::Rennsport => "Rennsport",
SupportedSim::LeMansUltimate => "Le Mans Ultimate",
}.to_string()
}
}
pub fn to_string_short(&self) -> String {
pub fn to_string_short(&self) -> &'static str {
match self {
SupportedSim::NoSim => "-",
SupportedSim::AssettoCorsa => "AC",
@ -71,6 +82,6 @@ impl SupportedSim {
SupportedSim::Automobilista2 => "AMS2",
SupportedSim::Rennsport => "RS",
SupportedSim::LeMansUltimate => "LMU",
}.to_string()
}
}
}