mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'juliancoffee/pvp_command' into 'master'
Implement /battlemode command and more See merge request veloren/veloren!2787
This commit is contained in:
commit
003b9f57ce
@ -47,6 +47,8 @@ pub enum ChatCommand {
|
|||||||
Alias,
|
Alias,
|
||||||
ApplyBuff,
|
ApplyBuff,
|
||||||
Ban,
|
Ban,
|
||||||
|
BattleMode,
|
||||||
|
BattleModeForce,
|
||||||
Build,
|
Build,
|
||||||
BuildAreaAdd,
|
BuildAreaAdd,
|
||||||
BuildAreaList,
|
BuildAreaList,
|
||||||
@ -321,6 +323,28 @@ impl ChatCommand {
|
|||||||
true for overwrite to alter an existing ban..",
|
true for overwrite to alter an existing ban..",
|
||||||
Some(Moderator),
|
Some(Moderator),
|
||||||
),
|
),
|
||||||
|
#[rustfmt::skip]
|
||||||
|
ChatCommand::BattleMode => cmd(
|
||||||
|
vec![Enum(
|
||||||
|
"battle mode",
|
||||||
|
vec!["pvp".to_owned(), "pve".to_owned()],
|
||||||
|
Optional,
|
||||||
|
)],
|
||||||
|
"Set your battle mode to:\n\
|
||||||
|
* pvp (player vs player)\n\
|
||||||
|
* pve (player vs environment).\n\
|
||||||
|
If called without arguments will show current battle mode.",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
ChatCommand::BattleModeForce => cmd(
|
||||||
|
vec![Enum(
|
||||||
|
"battle mode",
|
||||||
|
vec!["pvp".to_owned(), "pve".to_owned()],
|
||||||
|
Required,
|
||||||
|
)],
|
||||||
|
"Change your battle mode flag without any checks",
|
||||||
|
Some(Admin),
|
||||||
|
),
|
||||||
ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", None),
|
ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", None),
|
||||||
ChatCommand::BuildAreaAdd => cmd(
|
ChatCommand::BuildAreaAdd => cmd(
|
||||||
vec![
|
vec![
|
||||||
@ -623,6 +647,8 @@ impl ChatCommand {
|
|||||||
ChatCommand::Alias => "alias",
|
ChatCommand::Alias => "alias",
|
||||||
ChatCommand::ApplyBuff => "buff",
|
ChatCommand::ApplyBuff => "buff",
|
||||||
ChatCommand::Ban => "ban",
|
ChatCommand::Ban => "ban",
|
||||||
|
ChatCommand::BattleMode => "battlemode",
|
||||||
|
ChatCommand::BattleModeForce => "battlemode_force",
|
||||||
ChatCommand::Build => "build",
|
ChatCommand::Build => "build",
|
||||||
ChatCommand::BuildAreaAdd => "build_area_add",
|
ChatCommand::BuildAreaAdd => "build_area_add",
|
||||||
ChatCommand::BuildAreaList => "build_area_list",
|
ChatCommand::BuildAreaList => "build_area_list",
|
||||||
|
@ -12,13 +12,13 @@ use crate::{
|
|||||||
},
|
},
|
||||||
poise::PoiseChange,
|
poise::PoiseChange,
|
||||||
skills::SkillGroupKind,
|
skills::SkillGroupKind,
|
||||||
Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health, HealthChange,
|
Alignment, Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health,
|
||||||
HealthSource, Inventory, Ori, Player, Poise, SkillSet, Stats,
|
HealthChange, HealthSource, Inventory, Ori, Player, Poise, SkillSet, Stats,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::utils::StageSection,
|
states::utils::StageSection,
|
||||||
uid::Uid,
|
uid::{Uid, UidAllocator},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ use rand::{thread_rng, Rng};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use specs::Entity as EcsEntity;
|
use specs::{saveload::MarkerAllocator, Entity as EcsEntity, ReadStorage};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use std::{ops::MulAssign, time::Duration};
|
use std::{ops::MulAssign, time::Duration};
|
||||||
#[cfg(not(target_arch = "wasm32"))] use vek::*;
|
#[cfg(not(target_arch = "wasm32"))] use vek::*;
|
||||||
@ -469,15 +469,56 @@ impl Attack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if we should allow negative effects from one player to another
|
/// Function that checks for unintentional PvP between players.
|
||||||
// FIXME: handle pets?
|
///
|
||||||
// This code works only with players.
|
/// Returns `false` if attack will create unintentional conflict,
|
||||||
// You still can kill someone's pet and
|
/// e.g. if player with PvE mode will harm pets of other players
|
||||||
// you still can be killed by someone's pet
|
/// or other players will do the same to such player.
|
||||||
pub fn may_harm(attacker: Option<&Player>, target: Option<&Player>) -> bool {
|
///
|
||||||
attacker
|
/// If both players have PvP mode enabled, interact with NPC and
|
||||||
.zip(target)
|
/// in any other case, this function will return `true`
|
||||||
.map_or(true, |(attacker, target)| attacker.may_harm(target))
|
// TODO: add parameter for doing self-harm?
|
||||||
|
pub fn may_harm(
|
||||||
|
alignments: &ReadStorage<Alignment>,
|
||||||
|
players: &ReadStorage<Player>,
|
||||||
|
uid_allocator: &UidAllocator,
|
||||||
|
attacker: Option<EcsEntity>,
|
||||||
|
target: EcsEntity,
|
||||||
|
) -> bool {
|
||||||
|
// Return owner entity if pet,
|
||||||
|
// or just return entity back otherwise
|
||||||
|
let owner_if_pet = |entity| {
|
||||||
|
let alignment = alignments.get(entity).copied();
|
||||||
|
if let Some(Alignment::Owned(uid)) = alignment {
|
||||||
|
// return original entity
|
||||||
|
// if can't get owner
|
||||||
|
uid_allocator
|
||||||
|
.retrieve_entity_internal(uid.into())
|
||||||
|
.unwrap_or(entity)
|
||||||
|
} else {
|
||||||
|
entity
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Just return ok if attacker is unknown, it's probably
|
||||||
|
// environment or command.
|
||||||
|
let attacker = match attacker {
|
||||||
|
Some(attacker) => attacker,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// "Dereference" to owner if this is a pet.
|
||||||
|
let attacker = owner_if_pet(attacker);
|
||||||
|
let target = owner_if_pet(target);
|
||||||
|
|
||||||
|
// Get player components
|
||||||
|
let attacker_info = players.get(attacker);
|
||||||
|
let target_info = players.get(target);
|
||||||
|
|
||||||
|
// Return `true` if not players.
|
||||||
|
attacker_info
|
||||||
|
.zip(target_info)
|
||||||
|
.map_or(true, |(a, t)| a.may_harm(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -3,7 +3,7 @@ use specs::{Component, DerefFlaggedStorage, NullStorage};
|
|||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::resources::BattleMode;
|
use crate::resources::{BattleMode, Time};
|
||||||
|
|
||||||
const MAX_ALIAS_LEN: usize = 32;
|
const MAX_ALIAS_LEN: usize = 32;
|
||||||
|
|
||||||
@ -20,6 +20,7 @@ pub enum DisconnectReason {
|
|||||||
pub struct Player {
|
pub struct Player {
|
||||||
pub alias: String,
|
pub alias: String,
|
||||||
pub battle_mode: BattleMode,
|
pub battle_mode: BattleMode,
|
||||||
|
pub last_battlemode_change: Option<Time>,
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,10 +31,16 @@ impl BattleMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new(alias: String, battle_mode: BattleMode, uuid: Uuid) -> Self {
|
pub fn new(
|
||||||
|
alias: String,
|
||||||
|
battle_mode: BattleMode,
|
||||||
|
uuid: Uuid,
|
||||||
|
last_battlemode_change: Option<Time>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
alias,
|
alias,
|
||||||
battle_mode,
|
battle_mode,
|
||||||
|
last_battlemode_change,
|
||||||
uuid,
|
uuid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ pub struct PlayerPhysicsSettings {
|
|||||||
///
|
///
|
||||||
/// May be removed when we will discover better way
|
/// May be removed when we will discover better way
|
||||||
/// to handle duels and murders
|
/// to handle duels and murders
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Deserialize, Serialize)]
|
||||||
pub enum BattleMode {
|
pub enum BattleMode {
|
||||||
PvP,
|
PvP,
|
||||||
PvE,
|
PvE,
|
||||||
|
@ -4,7 +4,7 @@ use common::{
|
|||||||
aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
|
aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
|
||||||
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
||||||
group::Group,
|
group::Group,
|
||||||
Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos,
|
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{Emitter, EventBus, ServerEvent},
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
@ -27,6 +27,7 @@ pub struct ReadData<'a> {
|
|||||||
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
char_states: ReadStorage<'a, CharacterState>,
|
char_states: ReadStorage<'a, CharacterState>,
|
||||||
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
groups: ReadStorage<'a, Group>,
|
groups: ReadStorage<'a, Group>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
@ -174,9 +175,9 @@ fn activate_aura(
|
|||||||
// TODO: this check will disable friendly fire with PvE switch.
|
// TODO: this check will disable friendly fire with PvE switch.
|
||||||
//
|
//
|
||||||
// Which means that you can't apply debuffs on you and your group
|
// Which means that you can't apply debuffs on you and your group
|
||||||
// even if it's intented mechanics.
|
// even if it's intented mechanic.
|
||||||
//
|
//
|
||||||
// Not that we have this for now, but think about this
|
// We don't have this for now, but think about this
|
||||||
// when we will add this.
|
// when we will add this.
|
||||||
let may_harm = || {
|
let may_harm = || {
|
||||||
let owner = match source {
|
let owner = match source {
|
||||||
@ -185,11 +186,13 @@ fn activate_aura(
|
|||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
owner.map_or(true, |attacker| {
|
combat::may_harm(
|
||||||
let attacker = read_data.players.get(attacker);
|
&read_data.alignments,
|
||||||
let target = read_data.players.get(target);
|
&read_data.players,
|
||||||
combat::may_harm(attacker, target)
|
&read_data.uid_allocator,
|
||||||
})
|
owner,
|
||||||
|
target,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
conditions_held && (kind.is_buff() || may_harm())
|
conditions_held && (kind.is_buff() || may_harm())
|
||||||
|
@ -2,8 +2,8 @@ use common::{
|
|||||||
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
||||||
comp::{
|
comp::{
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource,
|
Alignment, Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health,
|
||||||
Inventory, Ori, Player, Pos, Scale, Stats,
|
HealthSource, Inventory, Ori, Player, Pos, Scale, Stats,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -36,6 +36,7 @@ pub struct ReadData<'a> {
|
|||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
orientations: ReadStorage<'a, Ori>,
|
orientations: ReadStorage<'a, Ori>,
|
||||||
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
scales: ReadStorage<'a, Scale>,
|
scales: ReadStorage<'a, Scale>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
@ -214,9 +215,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// PvP check
|
||||||
let may_harm = combat::may_harm(
|
let may_harm = combat::may_harm(
|
||||||
beam_owner.and_then(|owner| read_data.players.get(owner)),
|
&read_data.alignments,
|
||||||
read_data.players.get(target),
|
&read_data.players,
|
||||||
|
&read_data.uid_allocator,
|
||||||
|
beam_owner,
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
let attack_options = AttackOptions {
|
let attack_options = AttackOptions {
|
||||||
// No luck with dodging beams
|
// No luck with dodging beams
|
||||||
|
@ -2,13 +2,13 @@ use common::{
|
|||||||
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
||||||
comp::{
|
comp::{
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Player, Pos,
|
Alignment, Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori,
|
||||||
Scale, Stats,
|
Player, Pos, Scale, Stats,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::Time,
|
resources::Time,
|
||||||
uid::Uid,
|
uid::{Uid, UidAllocator},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
GroupTarget,
|
GroupTarget,
|
||||||
};
|
};
|
||||||
@ -21,11 +21,13 @@ use vek::*;
|
|||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
time: Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
orientations: ReadStorage<'a, Ori>,
|
orientations: ReadStorage<'a, Ori>,
|
||||||
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
scales: ReadStorage<'a, Scale>,
|
scales: ReadStorage<'a, Scale>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
@ -162,9 +164,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
char_state: read_data.char_states.get(target),
|
char_state: read_data.char_states.get(target),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PvP check
|
||||||
let may_harm = combat::may_harm(
|
let may_harm = combat::may_harm(
|
||||||
read_data.players.get(attacker),
|
&read_data.alignments,
|
||||||
read_data.players.get(target),
|
&read_data.players,
|
||||||
|
&read_data.uid_allocator,
|
||||||
|
Some(attacker),
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
|
|
||||||
let attack_options = AttackOptions {
|
let attack_options = AttackOptions {
|
||||||
|
@ -2,8 +2,8 @@ use common::{
|
|||||||
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
||||||
comp::{
|
comp::{
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
projectile, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory,
|
projectile, Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource,
|
||||||
Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{Emitter, EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -31,6 +31,7 @@ pub struct ReadData<'a> {
|
|||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
physics_states: ReadStorage<'a, PhysicsState>,
|
physics_states: ReadStorage<'a, PhysicsState>,
|
||||||
velocities: ReadStorage<'a, Vel>,
|
velocities: ReadStorage<'a, Vel>,
|
||||||
inventories: ReadStorage<'a, Inventory>,
|
inventories: ReadStorage<'a, Inventory>,
|
||||||
@ -299,9 +300,13 @@ fn dispatch_hit(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PvP check
|
||||||
let may_harm = combat::may_harm(
|
let may_harm = combat::may_harm(
|
||||||
owner.and_then(|owner| read_data.players.get(owner)),
|
&read_data.alignments,
|
||||||
read_data.players.get(target),
|
&read_data.players,
|
||||||
|
&read_data.uid_allocator,
|
||||||
|
owner,
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
|
|
||||||
let attack_options = AttackOptions {
|
let attack_options = AttackOptions {
|
||||||
|
@ -2,8 +2,8 @@ use common::{
|
|||||||
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
|
||||||
comp::{
|
comp::{
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, Ori,
|
Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory,
|
||||||
PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
Ori, PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -31,6 +31,7 @@ pub struct ReadData<'a> {
|
|||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
orientations: ReadStorage<'a, Ori>,
|
orientations: ReadStorage<'a, Ori>,
|
||||||
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
scales: ReadStorage<'a, Scale>,
|
scales: ReadStorage<'a, Scale>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
@ -209,9 +210,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
char_state: read_data.character_states.get(target),
|
char_state: read_data.character_states.get(target),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PvP check
|
||||||
let may_harm = combat::may_harm(
|
let may_harm = combat::may_harm(
|
||||||
shockwave_owner.and_then(|owner| read_data.players.get(owner)),
|
&read_data.alignments,
|
||||||
read_data.players.get(target),
|
&read_data.players,
|
||||||
|
&read_data.uid_allocator,
|
||||||
|
shockwave_owner,
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
let attack_options = AttackOptions {
|
let attack_options = AttackOptions {
|
||||||
// Trying roll during earthquake isn't the best idea
|
// Trying roll during earthquake isn't the best idea
|
||||||
|
@ -3,12 +3,15 @@
|
|||||||
//! in [do_command].
|
//! in [do_command].
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
client::Client,
|
||||||
|
login_provider::LoginProvider,
|
||||||
settings::{
|
settings::{
|
||||||
Ban, BanAction, BanInfo, EditableSetting, SettingError, WhitelistInfo, WhitelistRecord,
|
Ban, BanAction, BanInfo, EditableSetting, SettingError, WhitelistInfo, WhitelistRecord,
|
||||||
},
|
},
|
||||||
sys::terrain::NpcData,
|
sys::terrain::NpcData,
|
||||||
|
wiring,
|
||||||
wiring::{Logic, OutputFormula},
|
wiring::{Logic, OutputFormula},
|
||||||
Server, SpawnPoint, StateExt,
|
Server, Settings, SpawnPoint, StateExt,
|
||||||
};
|
};
|
||||||
use assets::AssetExt;
|
use assets::AssetExt;
|
||||||
use authc::Uuid;
|
use authc::Uuid;
|
||||||
@ -31,7 +34,7 @@ use common::{
|
|||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
generation::EntityInfo,
|
generation::EntityInfo,
|
||||||
npc::{self, get_npc_name},
|
npc::{self, get_npc_name},
|
||||||
resources::{PlayerPhysicsSettings, TimeOfDay},
|
resources::{BattleMode, PlayerPhysicsSettings, Time, TimeOfDay},
|
||||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
@ -52,7 +55,6 @@ use vek::*;
|
|||||||
use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement};
|
use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement};
|
||||||
use world::util::Sampler;
|
use world::util::Sampler;
|
||||||
|
|
||||||
use crate::{client::Client, login_provider::LoginProvider, wiring};
|
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
pub trait ChatCommandExt {
|
pub trait ChatCommandExt {
|
||||||
@ -113,6 +115,8 @@ fn do_command(
|
|||||||
ChatCommand::Alias => handle_alias,
|
ChatCommand::Alias => handle_alias,
|
||||||
ChatCommand::ApplyBuff => handle_apply_buff,
|
ChatCommand::ApplyBuff => handle_apply_buff,
|
||||||
ChatCommand::Ban => handle_ban,
|
ChatCommand::Ban => handle_ban,
|
||||||
|
ChatCommand::BattleMode => handle_battlemode,
|
||||||
|
ChatCommand::BattleModeForce => handle_battlemode_force,
|
||||||
ChatCommand::Build => handle_build,
|
ChatCommand::Build => handle_build,
|
||||||
ChatCommand::BuildAreaAdd => handle_build_area_add,
|
ChatCommand::BuildAreaAdd => handle_build_area_add,
|
||||||
ChatCommand::BuildAreaList => handle_build_area_list,
|
ChatCommand::BuildAreaList => handle_build_area_list,
|
||||||
@ -3094,6 +3098,145 @@ fn handle_ban(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_battlemode(
|
||||||
|
server: &mut Server,
|
||||||
|
client: EcsEntity,
|
||||||
|
target: EcsEntity,
|
||||||
|
args: Vec<String>,
|
||||||
|
_action: &ChatCommand,
|
||||||
|
) -> CmdResult<()> {
|
||||||
|
// TODO: discuss time
|
||||||
|
const COOLDOWN: f64 = 60.0 * 5.0;
|
||||||
|
|
||||||
|
let ecs = server.state.ecs();
|
||||||
|
let time = ecs.read_resource::<Time>();
|
||||||
|
let settings = ecs.read_resource::<Settings>();
|
||||||
|
if let Some(mode) = parse_args!(args, String) {
|
||||||
|
if !settings.battle_mode.allow_choosing() {
|
||||||
|
return Err("Command disabled in server settings".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
let in_town = {
|
||||||
|
// get chunk position
|
||||||
|
let pos = position(server, target, "target")?;
|
||||||
|
let wpos = pos.0.xy().map(|x| x as i32);
|
||||||
|
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |wpos, size: u32| {
|
||||||
|
wpos / size as i32
|
||||||
|
});
|
||||||
|
server.world.civs().sites().any(|site| {
|
||||||
|
// empirical
|
||||||
|
const RADIUS: f32 = 9.0;
|
||||||
|
let delta = site
|
||||||
|
.center
|
||||||
|
.map(|x| x as f32)
|
||||||
|
.distance(chunk_pos.map(|x| x as f32));
|
||||||
|
delta < RADIUS
|
||||||
|
})
|
||||||
|
};
|
||||||
|
// just skip this check, if worldgen is disabled
|
||||||
|
#[cfg(not(feature = "worldgen"))]
|
||||||
|
let in_town = true;
|
||||||
|
|
||||||
|
if !in_town {
|
||||||
|
return Err("You need to be in town to change battle mode!".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut players = ecs.write_storage::<comp::Player>();
|
||||||
|
let mut player_info = players.get_mut(target).ok_or_else(|| {
|
||||||
|
error!("Can't get player component for player");
|
||||||
|
"Error!"
|
||||||
|
})?;
|
||||||
|
if let Some(Time(last_change)) = player_info.last_battlemode_change {
|
||||||
|
let Time(time) = *time;
|
||||||
|
let elapsed = time - last_change;
|
||||||
|
if elapsed < COOLDOWN {
|
||||||
|
let msg = format!(
|
||||||
|
"Cooldown period active. Try again in {:.0} seconds",
|
||||||
|
COOLDOWN - elapsed,
|
||||||
|
);
|
||||||
|
return Err(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mode = match mode.as_str() {
|
||||||
|
"pvp" => BattleMode::PvP,
|
||||||
|
"pve" => BattleMode::PvE,
|
||||||
|
_ => return Err("Available modes: pvp, pve".to_owned()),
|
||||||
|
};
|
||||||
|
if player_info.battle_mode == mode {
|
||||||
|
return Err("Attempted to set the same battlemode".to_owned());
|
||||||
|
}
|
||||||
|
player_info.battle_mode = mode;
|
||||||
|
player_info.last_battlemode_change = Some(*time);
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ServerGeneral::server_msg(
|
||||||
|
ChatType::CommandInfo,
|
||||||
|
format!("New battle mode: {:?}", mode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let players = ecs.read_storage::<comp::Player>();
|
||||||
|
let player = players.get(target).ok_or_else(|| {
|
||||||
|
error!("Can't get player component for player");
|
||||||
|
"Error!"
|
||||||
|
})?;
|
||||||
|
let mut msg = format!("Current battle mode: {:?}.", player.battle_mode);
|
||||||
|
if settings.battle_mode.allow_choosing() {
|
||||||
|
msg.push_str(" Possible to change.");
|
||||||
|
} else {
|
||||||
|
msg.push_str(" Global.");
|
||||||
|
}
|
||||||
|
if let Some(change) = player.last_battlemode_change {
|
||||||
|
let Time(time) = *time;
|
||||||
|
let Time(change) = change;
|
||||||
|
let elapsed = time - change;
|
||||||
|
let next = COOLDOWN - elapsed;
|
||||||
|
let notice = format!(" Next change will be available in: {:.0} seconds", next);
|
||||||
|
msg.push_str(¬ice);
|
||||||
|
}
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ServerGeneral::server_msg(ChatType::CommandInfo, msg),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_battlemode_force(
|
||||||
|
server: &mut Server,
|
||||||
|
client: EcsEntity,
|
||||||
|
target: EcsEntity,
|
||||||
|
args: Vec<String>,
|
||||||
|
action: &ChatCommand,
|
||||||
|
) -> CmdResult<()> {
|
||||||
|
let ecs = server.state.ecs();
|
||||||
|
let settings = ecs.read_resource::<Settings>();
|
||||||
|
if !settings.battle_mode.allow_choosing() {
|
||||||
|
return Err("Command disabled in server settings".to_owned());
|
||||||
|
}
|
||||||
|
let mode = parse_args!(args, String).ok_or_else(|| action.help_string())?;
|
||||||
|
let mode = match mode.as_str() {
|
||||||
|
"pvp" => BattleMode::PvP,
|
||||||
|
"pve" => BattleMode::PvE,
|
||||||
|
_ => return Err("Available modes: pvp, pve".to_owned()),
|
||||||
|
};
|
||||||
|
let mut players = ecs.write_storage::<comp::Player>();
|
||||||
|
let mut player_info = players
|
||||||
|
.get_mut(target)
|
||||||
|
.ok_or("Cannot get player component for target")?;
|
||||||
|
player_info.battle_mode = mode;
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ServerGeneral::server_msg(
|
||||||
|
ChatType::CommandInfo,
|
||||||
|
format!("Set battle mode to: {:?}", mode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_unban(
|
fn handle_unban(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
client: EcsEntity,
|
client: EcsEntity,
|
||||||
|
@ -873,6 +873,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
|||||||
let energies = &ecs.read_storage::<comp::Energy>();
|
let energies = &ecs.read_storage::<comp::Energy>();
|
||||||
let combos = &ecs.read_storage::<comp::Combo>();
|
let combos = &ecs.read_storage::<comp::Combo>();
|
||||||
let inventories = &ecs.read_storage::<comp::Inventory>();
|
let inventories = &ecs.read_storage::<comp::Inventory>();
|
||||||
|
let alignments = &ecs.read_storage::<Alignment>();
|
||||||
|
let uid_allocator = &ecs.read_resource::<UidAllocator>();
|
||||||
let players = &ecs.read_storage::<comp::Player>();
|
let players = &ecs.read_storage::<comp::Player>();
|
||||||
for (
|
for (
|
||||||
entity_b,
|
entity_b,
|
||||||
@ -942,9 +944,13 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
|||||||
char_state: char_state_b_maybe,
|
char_state: char_state_b_maybe,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PvP check
|
||||||
let may_harm = combat::may_harm(
|
let may_harm = combat::may_harm(
|
||||||
owner_entity.and_then(|owner| players.get(owner)),
|
alignments,
|
||||||
players.get(entity_b),
|
players,
|
||||||
|
uid_allocator,
|
||||||
|
owner_entity,
|
||||||
|
entity_b,
|
||||||
);
|
);
|
||||||
let attack_options = combat::AttackOptions {
|
let attack_options = combat::AttackOptions {
|
||||||
// cool guyz maybe don't look at explosions
|
// cool guyz maybe don't look at explosions
|
||||||
@ -968,6 +974,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
RadiusEffect::Entity(mut effect) => {
|
RadiusEffect::Entity(mut effect) => {
|
||||||
|
let alignments = &ecs.read_storage::<Alignment>();
|
||||||
|
let uid_allocator = &ecs.read_resource::<UidAllocator>();
|
||||||
let players = &ecs.read_storage::<comp::Player>();
|
let players = &ecs.read_storage::<comp::Player>();
|
||||||
for (entity_b, pos_b, body_b_maybe) in (
|
for (entity_b, pos_b, body_b_maybe) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
@ -983,23 +991,18 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
|||||||
1.0 - distance_squared / explosion.radius.powi(2)
|
1.0 - distance_squared / explosion.radius.powi(2)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Player check only accounts for PvP/PvE flag.
|
// Player check only accounts for PvP/PvE flag, but bombs
|
||||||
|
// are intented to do friendly fire.
|
||||||
//
|
//
|
||||||
// But bombs are intented to do
|
// What exactly is friendly fire is subject to discussion.
|
||||||
// friendly fire.
|
|
||||||
//
|
|
||||||
// What exactly friendly fire is subject to discussion.
|
|
||||||
// As we probably want to minimize possibility of being dick
|
// As we probably want to minimize possibility of being dick
|
||||||
// even to your group members, the only exception is when
|
// even to your group members, the only exception is when
|
||||||
// you want to harm yourself.
|
// you want to harm yourself.
|
||||||
//
|
//
|
||||||
// This can be changed later.
|
// This can be changed later.
|
||||||
let may_harm = || {
|
let may_harm = || {
|
||||||
owner_entity.map_or(false, |attacker| {
|
combat::may_harm(alignments, players, uid_allocator, owner_entity, entity_b)
|
||||||
let attacker_player = players.get(attacker);
|
|| owner_entity.map_or(true, |entity_a| entity_a == entity_b)
|
||||||
let target_player = players.get(entity_b);
|
|
||||||
combat::may_harm(attacker_player, target_player) || attacker == entity_b
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
if strength > 0.0 {
|
if strength > 0.0 {
|
||||||
let is_alive = ecs
|
let is_alive = ecs
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::Event;
|
use super::Event;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::Client, metrics::PlayerMetrics, persistence::character_updater::CharacterUpdater,
|
client::Client, metrics::PlayerMetrics, persistence::character_updater::CharacterUpdater,
|
||||||
presence::Presence, state_ext::StateExt, Server,
|
presence::Presence, state_ext::StateExt, BattleModeBuffer, Server,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
@ -201,13 +201,17 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
|||||||
Some(skill_set),
|
Some(skill_set),
|
||||||
Some(inventory),
|
Some(inventory),
|
||||||
Some(player_uid),
|
Some(player_uid),
|
||||||
|
Some(player_info),
|
||||||
mut character_updater,
|
mut character_updater,
|
||||||
|
mut battlemode_buffer,
|
||||||
) = (
|
) = (
|
||||||
state.read_storage::<Presence>().get(entity),
|
state.read_storage::<Presence>().get(entity),
|
||||||
state.read_storage::<comp::SkillSet>().get(entity),
|
state.read_storage::<comp::SkillSet>().get(entity),
|
||||||
state.read_storage::<comp::Inventory>().get(entity),
|
state.read_storage::<comp::Inventory>().get(entity),
|
||||||
state.read_storage::<Uid>().get(entity),
|
state.read_storage::<Uid>().get(entity),
|
||||||
|
state.read_storage::<comp::Player>().get(entity),
|
||||||
state.ecs().fetch_mut::<CharacterUpdater>(),
|
state.ecs().fetch_mut::<CharacterUpdater>(),
|
||||||
|
state.ecs().fetch_mut::<BattleModeBuffer>(),
|
||||||
) {
|
) {
|
||||||
match presence.kind {
|
match presence.kind {
|
||||||
PresenceKind::Character(char_id) => {
|
PresenceKind::Character(char_id) => {
|
||||||
@ -216,6 +220,12 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
|
|||||||
.read_storage::<common::comp::Waypoint>()
|
.read_storage::<common::comp::Waypoint>()
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.cloned();
|
.cloned();
|
||||||
|
// Store last battle mode change
|
||||||
|
if let Some(change) = player_info.last_battlemode_change {
|
||||||
|
let mode = player_info.battle_mode;
|
||||||
|
let save = (mode, change);
|
||||||
|
battlemode_buffer.push(char_id, save);
|
||||||
|
}
|
||||||
|
|
||||||
// Get player's pets
|
// Get player's pets
|
||||||
let alignments = state.ecs().read_storage::<comp::Alignment>();
|
let alignments = state.ecs().read_storage::<comp::Alignment>();
|
||||||
|
@ -64,12 +64,13 @@ use crate::{
|
|||||||
use common::grid::Grid;
|
use common::grid::Grid;
|
||||||
use common::{
|
use common::{
|
||||||
assets::AssetExt,
|
assets::AssetExt,
|
||||||
|
character::CharacterId,
|
||||||
cmd::ChatCommand,
|
cmd::ChatCommand,
|
||||||
comp,
|
comp,
|
||||||
comp::{item::MaterialStatManifest, CharacterAbility},
|
comp::{item::MaterialStatManifest, CharacterAbility},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
recipe::default_recipe_book,
|
recipe::default_recipe_book,
|
||||||
resources::TimeOfDay,
|
resources::{BattleMode, Time, TimeOfDay},
|
||||||
rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
terrain::{TerrainChunk, TerrainChunkSize},
|
terrain::{TerrainChunk, TerrainChunkSize},
|
||||||
@ -109,6 +110,7 @@ use crate::{
|
|||||||
persistence::{DatabaseSettings, SqlLogMode},
|
persistence::{DatabaseSettings, SqlLogMode},
|
||||||
sys::terrain,
|
sys::terrain,
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
@ -152,6 +154,26 @@ enum DisconnectType {
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct TickStart(Instant);
|
pub struct TickStart(Instant);
|
||||||
|
|
||||||
|
/// Store of BattleMode cooldowns for players while they go offline
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
pub struct BattleModeBuffer {
|
||||||
|
map: HashMap<CharacterId, (BattleMode, Time)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BattleModeBuffer {
|
||||||
|
pub fn push(&mut self, char_id: CharacterId, save: (BattleMode, Time)) {
|
||||||
|
self.map.insert(char_id, save);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, char_id: &CharacterId) -> Option<&(BattleMode, Time)> {
|
||||||
|
self.map.get(char_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self, char_id: &CharacterId) -> Option<(BattleMode, Time)> {
|
||||||
|
self.map.remove(char_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
state: State,
|
state: State,
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
@ -196,8 +218,10 @@ impl Server {
|
|||||||
let ecs_system_metrics = EcsSystemMetrics::new(®istry).unwrap();
|
let ecs_system_metrics = EcsSystemMetrics::new(®istry).unwrap();
|
||||||
let tick_metrics = TickMetrics::new(®istry).unwrap();
|
let tick_metrics = TickMetrics::new(®istry).unwrap();
|
||||||
let physics_metrics = PhysicsMetrics::new(®istry).unwrap();
|
let physics_metrics = PhysicsMetrics::new(®istry).unwrap();
|
||||||
|
let battlemode_buffer = BattleModeBuffer::default();
|
||||||
|
|
||||||
let mut state = State::server();
|
let mut state = State::server();
|
||||||
|
state.ecs_mut().insert(battlemode_buffer);
|
||||||
state.ecs_mut().insert(settings.clone());
|
state.ecs_mut().insert(settings.clone());
|
||||||
state.ecs_mut().insert(editable_settings);
|
state.ecs_mut().insert(editable_settings);
|
||||||
state.ecs_mut().insert(DataDir {
|
state.ecs_mut().insert(DataDir {
|
||||||
|
@ -40,6 +40,28 @@ pub struct X509FilePair {
|
|||||||
pub key: PathBuf,
|
pub key: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub enum ServerBattleMode {
|
||||||
|
Global(BattleMode),
|
||||||
|
PerPlayer { default: BattleMode },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerBattleMode {
|
||||||
|
pub fn allow_choosing(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ServerBattleMode::Global { .. } => false,
|
||||||
|
ServerBattleMode::PerPlayer { .. } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_mode(&self) -> BattleMode {
|
||||||
|
match self {
|
||||||
|
ServerBattleMode::Global(mode) => *mode,
|
||||||
|
ServerBattleMode::PerPlayer { default: mode } => *mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
@ -49,7 +71,7 @@ pub struct Settings {
|
|||||||
pub quic_files: Option<X509FilePair>,
|
pub quic_files: Option<X509FilePair>,
|
||||||
pub max_players: usize,
|
pub max_players: usize,
|
||||||
pub world_seed: u32,
|
pub world_seed: u32,
|
||||||
pub battle_mode: BattleMode,
|
pub battle_mode: ServerBattleMode,
|
||||||
pub server_name: String,
|
pub server_name: String,
|
||||||
pub start_time: f64,
|
pub start_time: f64,
|
||||||
/// When set to None, loads the default map file (if available); otherwise,
|
/// When set to None, loads the default map file (if available); otherwise,
|
||||||
@ -79,7 +101,7 @@ impl Default for Settings {
|
|||||||
world_seed: DEFAULT_WORLD_SEED,
|
world_seed: DEFAULT_WORLD_SEED,
|
||||||
server_name: "Veloren Alpha".into(),
|
server_name: "Veloren Alpha".into(),
|
||||||
max_players: 100,
|
max_players: 100,
|
||||||
battle_mode: BattleMode::PvP,
|
battle_mode: ServerBattleMode::Global(BattleMode::PvP),
|
||||||
start_time: 9.0 * 3600.0,
|
start_time: 9.0 * 3600.0,
|
||||||
map_file: None,
|
map_file: None,
|
||||||
max_view_distance: Some(65),
|
max_view_distance: Some(65),
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
presence::{Presence, RepositionOnChunkLoad},
|
presence::{Presence, RepositionOnChunkLoad},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
sys::sentinel::DeletedEntities,
|
sys::sentinel::DeletedEntities,
|
||||||
wiring, SpawnPoint,
|
wiring, BattleModeBuffer, SpawnPoint,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
character::CharacterId,
|
character::CharacterId,
|
||||||
@ -496,8 +496,8 @@ impl StateExt for State {
|
|||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
// NOTE: By fetching the player_uid, we validated that the entity exists, and we
|
// NOTE: By fetching the player_uid, we validated that the entity exists,
|
||||||
// call nothing that can delete it in any of the subsequent
|
// and we call nothing that can delete it in any of the subsequent
|
||||||
// commands, so we can assume that all of these calls succeed,
|
// commands, so we can assume that all of these calls succeed,
|
||||||
// justifying ignoring the result of insertion.
|
// justifying ignoring the result of insertion.
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Collider::Box {
|
self.write_component_ignore_entity_dead(entity, comp::Collider::Box {
|
||||||
@ -567,6 +567,36 @@ impl StateExt for State {
|
|||||||
} else {
|
} else {
|
||||||
warn!("Player has no pos, cannot load {} pets", pets.len());
|
warn!("Player has no pos, cannot load {} pets", pets.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let presences = self.ecs().read_storage::<Presence>();
|
||||||
|
let presence = presences.get(entity);
|
||||||
|
if let Some(Presence {
|
||||||
|
kind: PresenceKind::Character(char_id),
|
||||||
|
..
|
||||||
|
}) = presence
|
||||||
|
{
|
||||||
|
let battlemode_buffer = self.ecs().fetch::<BattleModeBuffer>();
|
||||||
|
let mut players = self.ecs().write_storage::<comp::Player>();
|
||||||
|
if let Some((mode, change)) = battlemode_buffer.get(char_id) {
|
||||||
|
if let Some(mut player_info) = players.get_mut(entity) {
|
||||||
|
player_info.battle_mode = *mode;
|
||||||
|
player_info.last_battlemode_change = Some(*change);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// FIXME:
|
||||||
|
// ???
|
||||||
|
//
|
||||||
|
// This probably shouldn't exist,
|
||||||
|
// but without this code, character gets battle_mode from
|
||||||
|
// another character on this account.
|
||||||
|
let settings = self.ecs().read_resource::<Settings>();
|
||||||
|
let mode = settings.battle_mode.default_mode();
|
||||||
|
if let Some(mut player_info) = players.get_mut(entity) {
|
||||||
|
player_info.battle_mode = mode;
|
||||||
|
player_info.last_battlemode_change = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let player = Player::new(username, read_data.settings.battle_mode, uuid);
|
// NOTE: this is just default value.
|
||||||
|
//
|
||||||
|
// It will be overwritten in ServerExt::update_character_data.
|
||||||
|
let battle_mode = read_data.settings.battle_mode.default_mode();
|
||||||
|
let player = Player::new(username, battle_mode, uuid, None);
|
||||||
|
|
||||||
let admin = read_data.editable_settings.admins.get(&uuid);
|
let admin = read_data.editable_settings.admins.get(&uuid);
|
||||||
|
|
||||||
if !player.is_valid() {
|
if !player.is_valid() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user