mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'AldanTanneo/discord-presence' into 'master'
Discord Activity See merge request veloren/veloren!3529
This commit is contained in:
commit
dfe2e1c085
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -128,6 +128,24 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8"
|
||||
|
||||
[[package]]
|
||||
name = "app_dirs2"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3564c46eaa62f4a4e50c98e2c698cbb95f95c08501bd2e3baf6ea73773ab9fc"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"ndk-context",
|
||||
"winapi 0.3.9",
|
||||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.3.2"
|
||||
@ -1567,6 +1585,31 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "discord-sdk"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e314b59ba110cb4fad80073f5276eb62cda4cc3caabe998f5d664b19fd6a7be0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app_dirs2",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"bitflags",
|
||||
"crossbeam-channel",
|
||||
"num-traits",
|
||||
"parking_lot 0.12.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.9",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.1.4"
|
||||
@ -6806,6 +6849,7 @@ dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-utils 0.8.8",
|
||||
"directories-next",
|
||||
"discord-sdk",
|
||||
"dispatch 0.1.4",
|
||||
"dot_vox",
|
||||
"egui",
|
||||
@ -7797,6 +7841,15 @@ dependencies = [
|
||||
"x11-dl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winres"
|
||||
version = "0.1.12"
|
||||
|
@ -11,6 +11,7 @@ common-controls = Controls
|
||||
common-video = Graphics
|
||||
common-sound = Sound
|
||||
common-chat = Chat
|
||||
common-networking = Networking
|
||||
common-resume = Resume
|
||||
common-characters = Characters
|
||||
common-close = Close
|
||||
@ -40,6 +41,7 @@ common-video_settings = Graphics Settings
|
||||
common-sound_settings = Sound Settings
|
||||
common-language_settings = Language Settings
|
||||
common-chat_settings = Chat Settings
|
||||
common-networking_settings = Networking Settings
|
||||
common-connection_lost =
|
||||
Connection lost!
|
||||
Did the server restart?
|
||||
@ -105,4 +107,4 @@ common-material-wood = Wood
|
||||
common-material-stone = Stone
|
||||
common-material-cloth = Cloth
|
||||
common-material-hide = Hide
|
||||
common-sprite-chest = Chest
|
||||
common-sprite-chest = Chest
|
||||
|
@ -132,4 +132,6 @@ hud-settings-region = Region
|
||||
hud-settings-say = Say
|
||||
hud-settings-all = All
|
||||
hud-settings-group_only = Group only
|
||||
hud-settings-reset_chat = Reset to Defaults
|
||||
hud-settings-reset_chat = Reset to Defaults
|
||||
hud-settings-third_party_integrations = Third-party Integrations
|
||||
hud-settings-enable_discord_integration = Enable Discord Integration
|
||||
|
@ -201,7 +201,7 @@ impl Default for Settings {
|
||||
metrics_address: SocketAddr::from((Ipv4Addr::LOCALHOST, 14005)),
|
||||
auth_server_address: Some("https://auth.veloren.net".into()),
|
||||
world_seed: DEFAULT_WORLD_SEED,
|
||||
server_name: "Veloren Alpha".into(),
|
||||
server_name: "Veloren Server".into(),
|
||||
max_players: 100,
|
||||
start_time: 9.0 * 3600.0,
|
||||
map_file: None,
|
||||
|
@ -32,9 +32,10 @@ tracy-memory = ["tracy"] # enables heap profiling with tracy
|
||||
plugins = ["client/plugins"]
|
||||
egui-ui = ["voxygen-egui", "egui", "egui_wgpu_backend", "egui_winit_platform"]
|
||||
shaderc-from-source = ["shaderc/build-from-source"]
|
||||
discord = ["discord-sdk"]
|
||||
|
||||
# We don't ship egui with published release builds so a separate feature is required that excludes it.
|
||||
default-publish = ["singleplayer", "native-dialog", "plugins", "simd"]
|
||||
default-publish = ["singleplayer", "native-dialog", "plugins", "simd", "discord"]
|
||||
# Temp for bug on current wgpu version that has access violation in vulkan when constructing egui pipeline
|
||||
default-no-egui = ["default-publish", "hot-reloading", "shaderc-from-source"]
|
||||
default = ["default-no-egui", "egui-ui"]
|
||||
@ -131,6 +132,9 @@ itertools = "0.10.0"
|
||||
tracing = "0.1"
|
||||
profiling = { version = "1.0.6", default-features = false, optional = true }
|
||||
|
||||
# Discord RPC
|
||||
discord-sdk = { version = "0.3.0", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
dispatch = "0.1.4"
|
||||
|
||||
|
304
voxygen/src/discord.rs
Normal file
304
voxygen/src/discord.rs
Normal file
@ -0,0 +1,304 @@
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use common::terrain::SiteKindMeta;
|
||||
use discord_sdk::{
|
||||
self as ds, activity,
|
||||
activity::{ActivityArgs, ActivityBuilder},
|
||||
};
|
||||
use tokio::{
|
||||
sync::mpsc::{unbounded_channel, UnboundedSender},
|
||||
time::{interval, MissedTickBehavior},
|
||||
};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
/// Discord app id
|
||||
///
|
||||
/// **Note:** currently a private app created for testing purposes, can be
|
||||
/// shared to a team or replaced entirely later on
|
||||
const DISCORD_APP_ID: ds::AppId = 1006661232465563698;
|
||||
|
||||
/// Discord presence update command
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ActivityUpdate {
|
||||
/// Clear the current Discord activity and exit the activity task
|
||||
Clear,
|
||||
/// Set the activity to "In Main Menu"
|
||||
MainMenu,
|
||||
/// Set the activity to "In Character Selection"
|
||||
CharacterSelection,
|
||||
/// Set the activity to "Playing Singleplayer"
|
||||
JoinSingleplayer,
|
||||
/// Set the activity to "Playing Multiplayer"
|
||||
JoinServer(String),
|
||||
/// Set the large asset text to the location name
|
||||
NewLocation {
|
||||
chunk_name: String,
|
||||
site: SiteKindMeta,
|
||||
},
|
||||
}
|
||||
|
||||
impl ActivityUpdate {
|
||||
/// Rich Presence asset keys: the backgrounds used in the main menu and
|
||||
/// loading screen
|
||||
///
|
||||
/// TODO: randomize images? use them according to the current biome?
|
||||
const ASSETS: [&'static str; 15] = [
|
||||
"bg_main", "bg_1", "bg_2", "bg_3", "bg_4", "bg_5", "bg_6", "bg_7", "bg_8", "bg_9", "bg_10",
|
||||
"bg_11", "bg_12", "bg_13", "bg_14",
|
||||
];
|
||||
/// Rich Presence character screen asset key
|
||||
const CHARACTER_SCREEN_ASSET: &'static str = "character_screen";
|
||||
/// Rich Presence logo asset key
|
||||
const LOGO_ASSET: &'static str = "logo";
|
||||
|
||||
/// Edit the current activity args according to the command in `self`.
|
||||
///
|
||||
/// - For `MainMenu`, `CharacterSelection`, `JoinSingleplayer` and
|
||||
/// `JoinServer(name)`: create a new activity and discard the previous one
|
||||
/// - For `NewLocation` and `LevelUp`: update the current activity
|
||||
fn edit_activity(self, args: &mut ActivityArgs) {
|
||||
use ActivityUpdate::*;
|
||||
|
||||
match self {
|
||||
Clear => (),
|
||||
MainMenu => {
|
||||
*args = ActivityBuilder::default()
|
||||
.start_timestamp(SystemTime::now())
|
||||
.state("Idle")
|
||||
.details("In Main Menu")
|
||||
.assets(
|
||||
activity::Assets::default().large(Self::LOGO_ASSET, Option::<&str>::None),
|
||||
)
|
||||
.into();
|
||||
},
|
||||
CharacterSelection => {
|
||||
*args = ActivityBuilder::default()
|
||||
.start_timestamp(SystemTime::now())
|
||||
.state("Idle")
|
||||
.details("In Character Selection")
|
||||
.assets(
|
||||
activity::Assets::default()
|
||||
.large(Self::CHARACTER_SCREEN_ASSET, Option::<&str>::None)
|
||||
.small(Self::LOGO_ASSET, Option::<&str>::None),
|
||||
)
|
||||
.into();
|
||||
},
|
||||
JoinSingleplayer => {
|
||||
*args = ActivityBuilder::default()
|
||||
.start_timestamp(SystemTime::now())
|
||||
.details("Playing Singleplayer")
|
||||
.assets(
|
||||
activity::Assets::default()
|
||||
.large(Self::ASSETS[9], Option::<&str>::None)
|
||||
.small(Self::LOGO_ASSET, Option::<&str>::None),
|
||||
)
|
||||
.into();
|
||||
},
|
||||
JoinServer(server_name) => {
|
||||
*args = ActivityBuilder::default()
|
||||
.start_timestamp(SystemTime::now())
|
||||
.state(format!("On {server_name}"))
|
||||
.details("Playing Multiplayer")
|
||||
.assets(
|
||||
activity::Assets::default()
|
||||
.large(Self::ASSETS[1], Option::<&str>::None)
|
||||
.small(Self::LOGO_ASSET, Option::<&str>::None),
|
||||
)
|
||||
.into();
|
||||
},
|
||||
NewLocation { chunk_name, site } => {
|
||||
use common::terrain::site::{
|
||||
DungeonKindMeta::*, SettlementKindMeta::*, SiteKindMeta::*,
|
||||
};
|
||||
|
||||
let location = match site {
|
||||
Dungeon(Old) => format!("Battling evil in {chunk_name}"),
|
||||
Dungeon(Gnarling) => format!("Hunting Gnarlings in {chunk_name}"),
|
||||
Cave => "In a Cave".to_string(),
|
||||
Settlement(Default) => format!("Visiting {chunk_name}"),
|
||||
Settlement(Cliff) => format!("Climbing the towers of {chunk_name}"),
|
||||
Settlement(Desert) => format!("Hiding from the sun in {chunk_name}"),
|
||||
_ => format!("In {chunk_name}"),
|
||||
};
|
||||
|
||||
args.activity.as_mut().map(|a| {
|
||||
a.assets.as_mut().map(|assets| {
|
||||
assets.large_text = Some(location);
|
||||
})
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A channel to the background task that updates the Discord activity.
|
||||
pub enum Discord {
|
||||
/// Active state, receiving updates
|
||||
Active {
|
||||
/// The channel to communicate with the tokio task
|
||||
channel: UnboundedSender<ActivityUpdate>,
|
||||
/// Current chunk name, cached to check for updates
|
||||
current_chunk_name: Option<String>,
|
||||
/// Current site, cached to check for updates
|
||||
current_site: SiteKindMeta,
|
||||
},
|
||||
/// Inactive state: either the Discord app could not be contacted, is not
|
||||
/// installed, or was disconnected
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl Discord {
|
||||
/// Start a background [tokio task](tokio::task) that will update the
|
||||
/// Discord activity every 4 seconds (due to rate limits) if it has
|
||||
/// changed.
|
||||
///
|
||||
/// The [`update`](Discord::update) method can be used on the returned
|
||||
/// struct to update the Discord activity via a channel command
|
||||
pub fn start(rt: &tokio::runtime::Runtime) -> Self {
|
||||
let (sender, mut receiver) = unbounded_channel::<ActivityUpdate>();
|
||||
|
||||
rt.spawn(async move {
|
||||
let (wheel, handler) = ds::wheel::Wheel::new(Box::new(|err| {
|
||||
warn!(error = ?err, "Encountered an error while connecting to Discord");
|
||||
}));
|
||||
|
||||
let mut user = wheel.user();
|
||||
|
||||
let discord = match ds::Discord::new(
|
||||
ds::DiscordApp::PlainId(DISCORD_APP_ID),
|
||||
ds::Subscriptions::ACTIVITY,
|
||||
Box::new(handler),
|
||||
) {
|
||||
Ok(ds) => {
|
||||
if let Err(err) = user.0.changed().await {
|
||||
warn!(err = ?err, "Could not execute handshake to Discord");
|
||||
// If no handshake is received, exit the task immediately
|
||||
return;
|
||||
}
|
||||
info!("Connected to Discord");
|
||||
ds
|
||||
},
|
||||
Err(err) => {
|
||||
info!(err = ?err, "Could not connect to Discord app");
|
||||
// If no Discord app was found, exit the task immediately
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let mut args = ActivityArgs::default();
|
||||
let mut has_changed = false;
|
||||
let mut interval = interval(Duration::from_secs(4));
|
||||
interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
|
||||
|
||||
loop {
|
||||
// Check every four seconds if the activity needs to change
|
||||
tokio::select! {
|
||||
biased; // to save the CPU cost of selecting a random branch
|
||||
|
||||
_ = interval.tick(), if has_changed => {
|
||||
has_changed = false;
|
||||
let activity = args.activity.clone();
|
||||
match discord.update_activity(args).await {
|
||||
Err(err) => {
|
||||
warn!(error = ?err, "Could not update Discord activity");
|
||||
}
|
||||
Ok(Some(new_activity)) => {
|
||||
debug!(new_activity = ?new_activity, "Updated Discord activity");
|
||||
},
|
||||
Ok(None) => ()
|
||||
}
|
||||
args = ActivityArgs::default();
|
||||
args.activity = activity;
|
||||
}
|
||||
update = receiver.recv() => match update {
|
||||
None | Some(ActivityUpdate::Clear) => {
|
||||
match discord.clear_activity().await {
|
||||
Ok(_) => {
|
||||
info!("Cleared Discord activity");
|
||||
},
|
||||
Err(err) => {
|
||||
warn!(error = ?err, "Failed to clear Discord activity")
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
Some(update) => {
|
||||
update.edit_activity(&mut args);
|
||||
has_changed = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Self::Active {
|
||||
channel: sender,
|
||||
current_chunk_name: None,
|
||||
current_site: SiteKindMeta::Void,
|
||||
}
|
||||
}
|
||||
|
||||
/// Send an activity update to the background task
|
||||
#[inline]
|
||||
fn update(&mut self, update: ActivityUpdate) {
|
||||
if let Self::Active { channel, .. } = self {
|
||||
// On error, turn itself into inactive to avoid sending unecessary updates
|
||||
if channel.send(update).is_err() {
|
||||
*self = Self::Inactive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the Discord activity
|
||||
#[inline]
|
||||
pub fn clear_activity(&mut self) {
|
||||
self.update(ActivityUpdate::Clear);
|
||||
*self = Discord::Inactive;
|
||||
}
|
||||
|
||||
/// Sets the current Discord activity to Main Menu
|
||||
#[inline]
|
||||
pub fn enter_main_menu(&mut self) { self.update(ActivityUpdate::MainMenu); }
|
||||
|
||||
/// Sets the current Discord activity to Character Selection
|
||||
#[inline]
|
||||
pub fn enter_character_selection(&mut self) { self.update(ActivityUpdate::CharacterSelection); }
|
||||
|
||||
/// Sets the current Discord activity to Singleplayer
|
||||
#[inline]
|
||||
pub fn join_singleplayer(&mut self) { self.update(ActivityUpdate::JoinSingleplayer); }
|
||||
|
||||
/// Sets the current Discord activity to Multiplayer with the corresponding
|
||||
/// server name
|
||||
#[inline]
|
||||
pub fn join_server(&mut self, server_name: String) {
|
||||
self.update(ActivityUpdate::JoinServer(server_name));
|
||||
}
|
||||
|
||||
/// Check the current location name and update it if it has changed
|
||||
#[inline]
|
||||
pub fn update_location(&mut self, chunk_name: &str, site: SiteKindMeta) {
|
||||
if let Self::Active {
|
||||
current_chunk_name,
|
||||
current_site,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
let different_name = current_chunk_name.as_deref() != Some(chunk_name);
|
||||
if different_name || *current_site != site {
|
||||
if different_name {
|
||||
*current_chunk_name = Some(chunk_name.to_string());
|
||||
}
|
||||
*current_site = site;
|
||||
self.update(ActivityUpdate::NewLocation {
|
||||
chunk_name: chunk_name.to_string(),
|
||||
site,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check wether the Discord activity is active and receiving updates
|
||||
#[inline]
|
||||
pub fn is_active(&self) -> bool { matches!(self, Self::Active { .. }) }
|
||||
}
|
@ -44,8 +44,6 @@ widget_ids! {
|
||||
auto_walk_behavior_list,
|
||||
camera_clamp_behavior_text,
|
||||
camera_clamp_behavior_list,
|
||||
player_physics_behavior_text,
|
||||
player_physics_behavior_list,
|
||||
stop_auto_walk_on_input_button,
|
||||
stop_auto_walk_on_input_label,
|
||||
auto_camera_button,
|
||||
@ -440,43 +438,6 @@ impl<'a> Widget for Gameplay<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Player physics behavior
|
||||
Text::new(
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.settings.player_physics_behavior"),
|
||||
)
|
||||
.down_from(state.ids.auto_walk_behavior_list, 10.0)
|
||||
.right_from(state.ids.camera_clamp_behavior_text, 118.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.player_physics_behavior_text, ui);
|
||||
|
||||
let player_physics_selected =
|
||||
self.global_state.settings.gameplay.player_physics_behavior as usize;
|
||||
|
||||
if let Some(clicked) = DropDownList::new(
|
||||
&["Client-authoritative", "Server-authoritative"],
|
||||
Some(player_physics_selected),
|
||||
)
|
||||
.w_h(200.0, 30.0)
|
||||
.color(MENU_BG)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_id(self.fonts.cyri.conrod_id)
|
||||
.down_from(state.ids.player_physics_behavior_text, 8.0)
|
||||
.set(state.ids.player_physics_behavior_list, ui)
|
||||
{
|
||||
match clicked {
|
||||
0 => events.push(ChangePlayerPhysicsBehavior {
|
||||
server_authoritative: false,
|
||||
}),
|
||||
_ => events.push(ChangePlayerPhysicsBehavior {
|
||||
server_authoritative: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Stop autowalk on input toggle
|
||||
let stop_auto_walk_on_input_toggle = ToggleButton::new(
|
||||
self.global_state.settings.gameplay.stop_auto_walk_on_input,
|
||||
|
@ -3,6 +3,7 @@ mod controls;
|
||||
mod gameplay;
|
||||
mod interface;
|
||||
mod language;
|
||||
mod networking;
|
||||
mod sound;
|
||||
mod video;
|
||||
|
||||
@ -39,6 +40,7 @@ widget_ids! {
|
||||
sound,
|
||||
language,
|
||||
chat,
|
||||
networking,
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +56,7 @@ pub enum SettingsTab {
|
||||
Gameplay,
|
||||
Controls,
|
||||
Lang,
|
||||
Networking,
|
||||
}
|
||||
impl SettingsTab {
|
||||
fn name_key(&self) -> &str {
|
||||
@ -65,6 +68,7 @@ impl SettingsTab {
|
||||
SettingsTab::Video => "common.video",
|
||||
SettingsTab::Sound => "common.sound",
|
||||
SettingsTab::Lang => "common.languages",
|
||||
SettingsTab::Networking => "common.networking",
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +81,7 @@ impl SettingsTab {
|
||||
SettingsTab::Video => "common.video_settings",
|
||||
SettingsTab::Sound => "common.sound_settings",
|
||||
SettingsTab::Lang => "common.language_settings",
|
||||
SettingsTab::Networking => "common.networking_settings",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,6 +326,16 @@ impl<'a> Widget for SettingsWindow<'a> {
|
||||
events.push(Event::SettingsChange(change.into()));
|
||||
}
|
||||
},
|
||||
SettingsTab::Networking => {
|
||||
for change in
|
||||
networking::Networking::new(global_state, imgs, fonts, localized_strings)
|
||||
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
|
||||
.wh_of(state.ids.settings_content_align)
|
||||
.set(state.ids.networking, ui)
|
||||
{
|
||||
events.push(Event::SettingsChange(change.into()));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
events
|
||||
|
263
voxygen/src/hud/settings_window/networking.rs
Normal file
263
voxygen/src/hud/settings_window/networking.rs
Normal file
@ -0,0 +1,263 @@
|
||||
use crate::{
|
||||
hud::{img_ids::Imgs, MENU_BG, TEXT_COLOR},
|
||||
session::settings_change::{Networking as NetworkingChange, Networking::*},
|
||||
ui::{fonts::Fonts, ImageSlider, ToggleButton},
|
||||
GlobalState,
|
||||
};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, DropDownList, Rectangle, Text},
|
||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use i18n::Localization;
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
window,
|
||||
window_r,
|
||||
vd_text,
|
||||
vd_slider,
|
||||
vd_value,
|
||||
player_physics_behavior_text,
|
||||
player_physics_behavior_list,
|
||||
lossy_terrain_compression_button,
|
||||
lossy_terrain_compression_label,
|
||||
third_party_integrations_title,
|
||||
enable_discord_integration_text,
|
||||
enable_discord_integration_button
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Networking<'a> {
|
||||
global_state: &'a GlobalState,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
localized_strings: &'a Localization,
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
impl<'a> Networking<'a> {
|
||||
pub fn new(
|
||||
global_state: &'a GlobalState,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
localized_strings: &'a Localization,
|
||||
) -> Self {
|
||||
Self {
|
||||
global_state,
|
||||
imgs,
|
||||
fonts,
|
||||
localized_strings,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Networking<'a> {
|
||||
type Event = Vec<NetworkingChange>;
|
||||
type State = State;
|
||||
type Style = ();
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
common_base::prof_span!("Networking::update");
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
let mut events = Vec::new();
|
||||
|
||||
Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT)
|
||||
.xy(args.rect.xy())
|
||||
.graphics_for(args.id)
|
||||
.scroll_kids()
|
||||
.scroll_kids_vertically()
|
||||
.set(state.ids.window, ui);
|
||||
Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT)
|
||||
.top_right()
|
||||
.parent(state.ids.window)
|
||||
.set(state.ids.window_r, ui);
|
||||
|
||||
// View Distance
|
||||
Text::new(&self.localized_strings.get("hud.settings.view_distance"))
|
||||
.top_left_with_margins_on(state.ids.window, 10.0, 10.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.vd_text, ui);
|
||||
|
||||
if let Some(new_val) = ImageSlider::discrete(
|
||||
self.global_state.settings.graphics.view_distance,
|
||||
1,
|
||||
65,
|
||||
self.imgs.slider_indicator,
|
||||
self.imgs.slider,
|
||||
)
|
||||
.w_h(104.0, 22.0)
|
||||
.down_from(state.ids.vd_text, 8.0)
|
||||
.track_breadth(12.0)
|
||||
.slider_length(10.0)
|
||||
.pad_track((5.0, 5.0))
|
||||
.set(state.ids.vd_slider, ui)
|
||||
{
|
||||
events.push(NetworkingChange::AdjustViewDistance(new_val));
|
||||
}
|
||||
|
||||
Text::new(&format!(
|
||||
"{}",
|
||||
self.global_state.settings.graphics.view_distance
|
||||
))
|
||||
.right_from(state.ids.vd_slider, 8.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.vd_value, ui);
|
||||
|
||||
// Player physics behavior
|
||||
Text::new(
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.settings.player_physics_behavior"),
|
||||
)
|
||||
.down_from(state.ids.vd_slider, 8.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.player_physics_behavior_text, ui);
|
||||
|
||||
let player_physics_selected = self
|
||||
.global_state
|
||||
.settings
|
||||
.networking
|
||||
.player_physics_behavior as usize;
|
||||
|
||||
if let Some(clicked) = DropDownList::new(
|
||||
&["Client-authoritative", "Server-authoritative"],
|
||||
Some(player_physics_selected),
|
||||
)
|
||||
.w_h(200.0, 30.0)
|
||||
.color(MENU_BG)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_id(self.fonts.cyri.conrod_id)
|
||||
.down_from(state.ids.player_physics_behavior_text, 8.0)
|
||||
.set(state.ids.player_physics_behavior_list, ui)
|
||||
{
|
||||
match clicked {
|
||||
0 => events.push(ChangePlayerPhysicsBehavior {
|
||||
server_authoritative: false,
|
||||
}),
|
||||
_ => events.push(ChangePlayerPhysicsBehavior {
|
||||
server_authoritative: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Lossy terrain compression
|
||||
Text::new(
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.settings.lossy_terrain_compression"),
|
||||
)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.right_from(state.ids.player_physics_behavior_text, 64.0)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.lossy_terrain_compression_label, ui);
|
||||
|
||||
let lossy_terrain_compression = ToggleButton::new(
|
||||
self.global_state
|
||||
.settings
|
||||
.networking
|
||||
.lossy_terrain_compression,
|
||||
self.imgs.checkbox,
|
||||
self.imgs.checkbox_checked,
|
||||
)
|
||||
.w_h(18.0, 18.0)
|
||||
.right_from(state.ids.lossy_terrain_compression_label, 10.0)
|
||||
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||
.set(state.ids.lossy_terrain_compression_button, ui);
|
||||
|
||||
if self
|
||||
.global_state
|
||||
.settings
|
||||
.networking
|
||||
.lossy_terrain_compression
|
||||
!= lossy_terrain_compression
|
||||
{
|
||||
events.push(NetworkingChange::ToggleLossyTerrainCompression(
|
||||
lossy_terrain_compression,
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
{
|
||||
// Third party integrations
|
||||
Text::new(
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.settings.third_party_integrations"),
|
||||
)
|
||||
.down_from(state.ids.player_physics_behavior_list, 16.0)
|
||||
.font_size(self.fonts.cyri.scale(18))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.third_party_integrations_title, ui);
|
||||
|
||||
// Toggle Discord integration
|
||||
let enable_discord_integration = ToggleButton::new(
|
||||
self.global_state
|
||||
.settings
|
||||
.networking
|
||||
.enable_discord_integration,
|
||||
self.imgs.checkbox,
|
||||
self.imgs.checkbox_checked,
|
||||
)
|
||||
.w_h(18.0, 18.0)
|
||||
.down_from(state.ids.third_party_integrations_title, 8.0)
|
||||
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||
.set(state.ids.enable_discord_integration_button, ui);
|
||||
|
||||
if self
|
||||
.global_state
|
||||
.settings
|
||||
.networking
|
||||
.enable_discord_integration
|
||||
!= enable_discord_integration
|
||||
{
|
||||
events.push(ToggleDiscordIntegration(
|
||||
!self
|
||||
.global_state
|
||||
.settings
|
||||
.networking
|
||||
.enable_discord_integration,
|
||||
));
|
||||
}
|
||||
|
||||
Text::new(
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.settings.enable_discord_integration"),
|
||||
)
|
||||
.right_from(state.ids.enable_discord_integration_button, 10.0)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.graphics_for(state.ids.enable_discord_integration_button)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.enable_discord_integration_text, ui);
|
||||
}
|
||||
|
||||
events
|
||||
}
|
||||
}
|
@ -102,8 +102,6 @@ widget_ids! {
|
||||
//
|
||||
particles_button,
|
||||
particles_label,
|
||||
lossy_terrain_compression_button,
|
||||
lossy_terrain_compression_label,
|
||||
weapon_trails_button,
|
||||
weapon_trails_label,
|
||||
flashing_lights_button,
|
||||
@ -1219,49 +1217,11 @@ impl<'a> Widget for Video<'a> {
|
||||
events.push(GraphicsChange::ToggleParticlesEnabled(particles_enabled));
|
||||
}
|
||||
|
||||
// Lossy terrain compression
|
||||
Text::new(
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.settings.lossy_terrain_compression"),
|
||||
)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.right_from(state.ids.particles_label, 64.0)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.lossy_terrain_compression_label, ui);
|
||||
|
||||
let lossy_terrain_compression = ToggleButton::new(
|
||||
self.global_state
|
||||
.settings
|
||||
.graphics
|
||||
.lossy_terrain_compression,
|
||||
self.imgs.checkbox,
|
||||
self.imgs.checkbox_checked,
|
||||
)
|
||||
.w_h(18.0, 18.0)
|
||||
.right_from(state.ids.lossy_terrain_compression_label, 10.0)
|
||||
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
|
||||
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
|
||||
.set(state.ids.lossy_terrain_compression_button, ui);
|
||||
|
||||
if self
|
||||
.global_state
|
||||
.settings
|
||||
.graphics
|
||||
.lossy_terrain_compression
|
||||
!= lossy_terrain_compression
|
||||
{
|
||||
events.push(GraphicsChange::ToggleLossyTerrainCompression(
|
||||
lossy_terrain_compression,
|
||||
));
|
||||
}
|
||||
|
||||
// Weapon trails
|
||||
Text::new(&self.localized_strings.get("hud.settings.weapon_trails"))
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.right_from(state.ids.lossy_terrain_compression_label, 64.0)
|
||||
.right_from(state.ids.particles_label, 64.0)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.weapon_trails_label, ui);
|
||||
|
||||
|
@ -22,6 +22,7 @@ pub mod audio;
|
||||
pub mod cmd;
|
||||
pub mod controller;
|
||||
mod credits;
|
||||
#[cfg(feature = "discord")] pub mod discord;
|
||||
mod ecs;
|
||||
pub mod error;
|
||||
pub mod game_input;
|
||||
@ -83,6 +84,9 @@ pub struct GlobalState {
|
||||
pub client_error: Option<String>,
|
||||
// Used to clear the shadow textures when entering a PlayState that doesn't utilise shadows
|
||||
pub clear_shadows_next_frame: bool,
|
||||
/// A channel that sends Discord activity updates to a background task
|
||||
#[cfg(feature = "discord")]
|
||||
pub discord: crate::discord::Discord,
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
|
@ -285,6 +285,13 @@ fn main() {
|
||||
#[cfg(feature = "egui-ui")]
|
||||
let egui_state = EguiState::new(&window);
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
let discord = if settings.networking.enable_discord_integration {
|
||||
veloren_voxygen::discord::Discord::start(&tokio_runtime)
|
||||
} else {
|
||||
veloren_voxygen::discord::Discord::Inactive
|
||||
};
|
||||
|
||||
let global_state = GlobalState {
|
||||
userdata_dir,
|
||||
config_dir,
|
||||
@ -306,6 +313,8 @@ fn main() {
|
||||
clipboard,
|
||||
client_error: None,
|
||||
clear_shadows_next_frame: false,
|
||||
#[cfg(feature = "discord")]
|
||||
discord,
|
||||
};
|
||||
|
||||
run::run(global_state, event_loop);
|
||||
|
@ -74,6 +74,9 @@ impl PlayState for CharSelectionState {
|
||||
|
||||
// Clear shadow textures since we don't render to them here
|
||||
global_state.clear_shadows_next_frame = true;
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
global_state.discord.enter_character_selection();
|
||||
}
|
||||
|
||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
|
||||
|
@ -82,6 +82,9 @@ impl PlayState for MainMenuState {
|
||||
// Set scale mode in case it was change
|
||||
self.main_menu_ui
|
||||
.set_scale_mode(global_state.settings.interface.ui_scale);
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
global_state.discord.enter_main_menu();
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match)] // TODO: remove when event match has multiple arms
|
||||
|
@ -151,6 +151,10 @@ fn handle_main_events_cleared(
|
||||
break;
|
||||
},
|
||||
PlayStateResult::Shutdown => {
|
||||
// Clear the Discord activity before shutting down
|
||||
#[cfg(feature = "discord")]
|
||||
global_state.discord.clear_activity();
|
||||
|
||||
debug!("Shutting down all states...");
|
||||
while states.last().is_some() {
|
||||
states.pop().map(|old_state| {
|
||||
|
@ -113,9 +113,9 @@ impl SessionState {
|
||||
let mut mumble_link = SharedLink::new("veloren", "veloren-voxygen");
|
||||
{
|
||||
let mut client = client.borrow_mut();
|
||||
client.request_player_physics(global_state.settings.gameplay.player_physics_behavior);
|
||||
client.request_player_physics(global_state.settings.networking.player_physics_behavior);
|
||||
client.request_lossy_terrain_compression(
|
||||
global_state.settings.graphics.lossy_terrain_compression,
|
||||
global_state.settings.networking.lossy_terrain_compression,
|
||||
);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
if let Some(uid) = client.uid() {
|
||||
@ -408,6 +408,23 @@ impl PlayState for SessionState {
|
||||
self.client.borrow_mut().send_chat(cmd.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
{
|
||||
// Update the Discord activity on client initialization
|
||||
#[cfg(feature = "singleplayer")]
|
||||
let singleplayer = global_state.singleplayer.is_some();
|
||||
#[cfg(not(feature = "singleplayer"))]
|
||||
let singleplayer = false;
|
||||
|
||||
if singleplayer {
|
||||
global_state.discord.join_singleplayer();
|
||||
} else {
|
||||
global_state
|
||||
.discord
|
||||
.join_server(self.client.borrow().server_info().name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
||||
@ -434,6 +451,17 @@ impl PlayState for SessionState {
|
||||
let client = self.client.borrow();
|
||||
let player_entity = client.entity();
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
if global_state.discord.is_active() {
|
||||
if let Some(chunk) = client.current_chunk() {
|
||||
if let Some(location_name) = chunk.meta().name() {
|
||||
global_state
|
||||
.discord
|
||||
.update_location(location_name, client.current_site());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if global_state.settings.gameplay.bow_zoom {
|
||||
let mut fov_scaling = 1.0;
|
||||
if let Some(comp::CharacterState::ChargedRanged(cr)) = client
|
||||
|
@ -62,7 +62,6 @@ pub enum Gameplay {
|
||||
ChangeFreeLookBehavior(PressBehavior),
|
||||
ChangeAutoWalkBehavior(PressBehavior),
|
||||
ChangeCameraClampBehavior(PressBehavior),
|
||||
ChangePlayerPhysicsBehavior { server_authoritative: bool },
|
||||
ChangeStopAutoWalkOnInput(bool),
|
||||
ChangeAutoCamera(bool),
|
||||
ChangeBowZoom(bool),
|
||||
@ -89,7 +88,6 @@ pub enum Graphics {
|
||||
|
||||
ChangeFullscreenMode(FullScreenSettings),
|
||||
ToggleParticlesEnabled(bool),
|
||||
ToggleLossyTerrainCompression(bool),
|
||||
ToggleWeaponTrailsEnabled(bool),
|
||||
AdjustWindowSize([u16; 2]),
|
||||
|
||||
@ -148,7 +146,16 @@ pub enum Language {
|
||||
ToggleEnglishFallback(bool),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub enum Networking {}
|
||||
pub enum Networking {
|
||||
AdjustViewDistance(u32),
|
||||
ChangePlayerPhysicsBehavior {
|
||||
server_authoritative: bool,
|
||||
},
|
||||
ToggleLossyTerrainCompression(bool),
|
||||
|
||||
#[cfg(feature = "discord")]
|
||||
ToggleDiscordIntegration(bool),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SettingsChange {
|
||||
@ -319,15 +326,6 @@ impl SettingsChange {
|
||||
Gameplay::ChangeCameraClampBehavior(behavior) => {
|
||||
settings.gameplay.camera_clamp_behavior = behavior;
|
||||
},
|
||||
Gameplay::ChangePlayerPhysicsBehavior {
|
||||
server_authoritative,
|
||||
} => {
|
||||
settings.gameplay.player_physics_behavior = server_authoritative;
|
||||
session_state
|
||||
.client
|
||||
.borrow_mut()
|
||||
.request_player_physics(server_authoritative);
|
||||
},
|
||||
Gameplay::ChangeStopAutoWalkOnInput(state) => {
|
||||
settings.gameplay.stop_auto_walk_on_input = state;
|
||||
},
|
||||
@ -424,13 +422,6 @@ impl SettingsChange {
|
||||
Graphics::ToggleParticlesEnabled(particles_enabled) => {
|
||||
settings.graphics.particles_enabled = particles_enabled;
|
||||
},
|
||||
Graphics::ToggleLossyTerrainCompression(lossy_terrain_compression) => {
|
||||
settings.graphics.lossy_terrain_compression = lossy_terrain_compression;
|
||||
session_state
|
||||
.client
|
||||
.borrow_mut()
|
||||
.request_lossy_terrain_compression(lossy_terrain_compression);
|
||||
},
|
||||
Graphics::ToggleWeaponTrailsEnabled(weapon_trails_enabled) => {
|
||||
settings.graphics.weapon_trails_enabled = weapon_trails_enabled;
|
||||
},
|
||||
@ -612,7 +603,56 @@ impl SettingsChange {
|
||||
.set_english_fallback(settings.language.use_english_fallback);
|
||||
},
|
||||
},
|
||||
SettingsChange::Networking(networking_change) => match networking_change {},
|
||||
SettingsChange::Networking(networking_change) => match networking_change {
|
||||
Networking::AdjustViewDistance(view_distance) => {
|
||||
session_state
|
||||
.client
|
||||
.borrow_mut()
|
||||
.set_view_distance(view_distance);
|
||||
settings.graphics.view_distance = view_distance;
|
||||
},
|
||||
Networking::ChangePlayerPhysicsBehavior {
|
||||
server_authoritative,
|
||||
} => {
|
||||
settings.networking.player_physics_behavior = server_authoritative;
|
||||
session_state
|
||||
.client
|
||||
.borrow_mut()
|
||||
.request_player_physics(server_authoritative);
|
||||
},
|
||||
Networking::ToggleLossyTerrainCompression(lossy_terrain_compression) => {
|
||||
settings.networking.lossy_terrain_compression = lossy_terrain_compression;
|
||||
session_state
|
||||
.client
|
||||
.borrow_mut()
|
||||
.request_lossy_terrain_compression(lossy_terrain_compression);
|
||||
},
|
||||
#[cfg(feature = "discord")]
|
||||
Networking::ToggleDiscordIntegration(enabled) => {
|
||||
use crate::discord::Discord;
|
||||
|
||||
settings.networking.enable_discord_integration = enabled;
|
||||
if enabled {
|
||||
global_state.discord = Discord::start(&global_state.tokio_runtime);
|
||||
|
||||
#[cfg(feature = "singleplayer")]
|
||||
let singleplayer = global_state.singleplayer.is_some();
|
||||
#[cfg(not(feature = "singleplayer"))]
|
||||
let singleplayer = false;
|
||||
|
||||
if singleplayer {
|
||||
global_state.discord.join_singleplayer();
|
||||
} else {
|
||||
global_state.discord.join_server(
|
||||
session_state.client.borrow().server_info().name.clone(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
global_state.discord.clear_activity();
|
||||
global_state.discord = Discord::Inactive;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
settings.save_to_file_warn(&global_state.config_dir);
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ pub struct GameplaySettings {
|
||||
pub free_look_behavior: PressBehavior,
|
||||
pub auto_walk_behavior: PressBehavior,
|
||||
pub camera_clamp_behavior: PressBehavior,
|
||||
pub player_physics_behavior: bool,
|
||||
pub stop_auto_walk_on_input: bool,
|
||||
pub auto_camera: bool,
|
||||
pub bow_zoom: bool,
|
||||
@ -32,7 +31,6 @@ impl Default for GameplaySettings {
|
||||
free_look_behavior: PressBehavior::Toggle,
|
||||
auto_walk_behavior: PressBehavior::Toggle,
|
||||
camera_clamp_behavior: PressBehavior::Toggle,
|
||||
player_physics_behavior: false,
|
||||
stop_auto_walk_on_input: true,
|
||||
auto_camera: false,
|
||||
bow_zoom: true,
|
||||
|
@ -33,7 +33,6 @@ pub struct GraphicsSettings {
|
||||
pub lod_distance: u32,
|
||||
pub sprite_render_distance: u32,
|
||||
pub particles_enabled: bool,
|
||||
pub lossy_terrain_compression: bool,
|
||||
pub weapon_trails_enabled: bool,
|
||||
pub figure_lod_render_distance: u32,
|
||||
pub max_fps: Fps,
|
||||
@ -55,7 +54,6 @@ impl Default for GraphicsSettings {
|
||||
lod_distance: 200,
|
||||
sprite_render_distance: 100,
|
||||
particles_enabled: true,
|
||||
lossy_terrain_compression: false,
|
||||
weapon_trails_enabled: true,
|
||||
figure_lod_render_distance: 300,
|
||||
max_fps: Fps::Max(60),
|
||||
|
@ -10,6 +10,9 @@ pub struct NetworkingSettings {
|
||||
pub default_server: String,
|
||||
pub trusted_auth_servers: HashSet<String>,
|
||||
pub use_quic: bool,
|
||||
pub player_physics_behavior: bool,
|
||||
pub lossy_terrain_compression: bool,
|
||||
pub enable_discord_integration: bool,
|
||||
}
|
||||
|
||||
impl Default for NetworkingSettings {
|
||||
@ -23,6 +26,9 @@ impl Default for NetworkingSettings {
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
use_quic: false,
|
||||
player_physics_behavior: false,
|
||||
lossy_terrain_compression: false,
|
||||
enable_discord_integration: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user