mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Trade implementation progress.
- State machine for modifying trades. - ServerGeneral/ClientGeneral messages.
This commit is contained in:
parent
e9b811b62b
commit
ae528124fc
@ -69,7 +69,11 @@ const PING_ROLLING_AVERAGE_SECS: usize = 10;
|
|||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Chat(comp::ChatMsg),
|
Chat(comp::ChatMsg),
|
||||||
InviteComplete { target: Uid, answer: InviteAnswer, kind: InviteKind },
|
InviteComplete {
|
||||||
|
target: Uid,
|
||||||
|
answer: InviteAnswer,
|
||||||
|
kind: InviteKind,
|
||||||
|
},
|
||||||
Disconnect,
|
Disconnect,
|
||||||
DisconnectionNotification(u64),
|
DisconnectionNotification(u64),
|
||||||
InventoryUpdated(InventoryUpdateEvent),
|
InventoryUpdated(InventoryUpdateEvent),
|
||||||
@ -534,7 +538,8 @@ impl Client {
|
|||||||
| ClientGeneral::TerrainChunkRequest { .. }
|
| ClientGeneral::TerrainChunkRequest { .. }
|
||||||
| ClientGeneral::UnlockSkill(_)
|
| ClientGeneral::UnlockSkill(_)
|
||||||
| ClientGeneral::RefundSkill(_)
|
| ClientGeneral::RefundSkill(_)
|
||||||
| ClientGeneral::UnlockSkillGroup(_) => &mut self.in_game_stream,
|
| ClientGeneral::UnlockSkillGroup(_)
|
||||||
|
| ClientGeneral::UpdatePendingTrade(_, _) => &mut self.in_game_stream,
|
||||||
//Always possible
|
//Always possible
|
||||||
ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => {
|
ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => {
|
||||||
&mut self.general_stream
|
&mut self.general_stream
|
||||||
@ -638,7 +643,11 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_dead(&self) -> bool {
|
pub fn is_dead(&self) -> bool {
|
||||||
self.state.ecs().read_storage::<comp::Health>().get(self.entity).map_or(false, |h| h.is_dead)
|
self.state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::Health>()
|
||||||
|
.get(self.entity)
|
||||||
|
.map_or(false, |h| h.is_dead)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick_up(&mut self, entity: EcsEntity) {
|
pub fn pick_up(&mut self, entity: EcsEntity) {
|
||||||
@ -674,7 +683,9 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(uid) = self.state.read_component_copied(counterparty) {
|
if let Some(uid) = self.state.read_component_copied(counterparty) {
|
||||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InitiateTrade(uid)));
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InitiateTrade(
|
||||||
|
uid,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -741,7 +752,9 @@ impl Client {
|
|||||||
|
|
||||||
pub fn max_group_size(&self) -> u32 { self.max_group_size }
|
pub fn max_group_size(&self) -> u32 { self.max_group_size }
|
||||||
|
|
||||||
pub fn group_invite(&self) -> Option<(Uid, std::time::Instant, std::time::Duration, InviteKind)> {
|
pub fn group_invite(
|
||||||
|
&self,
|
||||||
|
) -> Option<(Uid, std::time::Instant, std::time::Duration, InviteKind)> {
|
||||||
self.group_invite
|
self.group_invite
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1472,7 +1485,11 @@ impl Client {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneral::GroupInvite { inviter, timeout, kind } => {
|
ServerGeneral::GroupInvite {
|
||||||
|
inviter,
|
||||||
|
timeout,
|
||||||
|
kind,
|
||||||
|
} => {
|
||||||
self.group_invite = Some((inviter, std::time::Instant::now(), timeout, kind));
|
self.group_invite = Some((inviter, std::time::Instant::now(), timeout, kind));
|
||||||
},
|
},
|
||||||
ServerGeneral::InvitePending(uid) => {
|
ServerGeneral::InvitePending(uid) => {
|
||||||
@ -1480,14 +1497,22 @@ impl Client {
|
|||||||
warn!("Received message about pending invite that was already pending");
|
warn!("Received message about pending invite that was already pending");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneral::InviteComplete { target, answer, kind } => {
|
ServerGeneral::InviteComplete {
|
||||||
|
target,
|
||||||
|
answer,
|
||||||
|
kind,
|
||||||
|
} => {
|
||||||
if !self.pending_invites.remove(&target) {
|
if !self.pending_invites.remove(&target) {
|
||||||
warn!(
|
warn!(
|
||||||
"Received completed invite message for invite that was not in the list of \
|
"Received completed invite message for invite that was not in the list of \
|
||||||
pending invites"
|
pending invites"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
frontend_events.push(Event::InviteComplete { target, answer, kind });
|
frontend_events.push(Event::InviteComplete {
|
||||||
|
target,
|
||||||
|
answer,
|
||||||
|
kind,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// Cleanup for when the client goes back to the `presence = None`
|
// Cleanup for when the client goes back to the `presence = None`
|
||||||
ServerGeneral::ExitInGameSuccess => {
|
ServerGeneral::ExitInGameSuccess => {
|
||||||
|
@ -4,6 +4,7 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
comp::{Skill, SkillGroupKind},
|
comp::{Skill, SkillGroupKind},
|
||||||
terrain::block::Block,
|
terrain::block::Block,
|
||||||
|
trade::TradeActionMsg,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -79,6 +80,7 @@ pub enum ClientGeneral {
|
|||||||
//Always possible
|
//Always possible
|
||||||
ChatMsg(String),
|
ChatMsg(String),
|
||||||
Terminate,
|
Terminate,
|
||||||
|
UpdatePendingTrade(usize, TradeActionMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientMsg {
|
impl ClientMsg {
|
||||||
@ -114,7 +116,8 @@ impl ClientMsg {
|
|||||||
| ClientGeneral::TerrainChunkRequest { .. }
|
| ClientGeneral::TerrainChunkRequest { .. }
|
||||||
| ClientGeneral::UnlockSkill(_)
|
| ClientGeneral::UnlockSkill(_)
|
||||||
| ClientGeneral::RefundSkill(_)
|
| ClientGeneral::RefundSkill(_)
|
||||||
| ClientGeneral::UnlockSkillGroup(_) => {
|
| ClientGeneral::UnlockSkillGroup(_)
|
||||||
|
| ClientGeneral::UpdatePendingTrade(_, _) => {
|
||||||
c_type == ClientType::Game && presence.is_some()
|
c_type == ClientType::Game && presence.is_some()
|
||||||
},
|
},
|
||||||
//Always possible
|
//Always possible
|
||||||
|
@ -7,6 +7,7 @@ use common::{
|
|||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::RecipeBook,
|
recipe::RecipeBook,
|
||||||
resources::TimeOfDay,
|
resources::TimeOfDay,
|
||||||
|
trade::PendingTrade,
|
||||||
terrain::{Block, TerrainChunk},
|
terrain::{Block, TerrainChunk},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -122,6 +123,7 @@ pub enum ServerGeneral {
|
|||||||
Disconnect(DisconnectReason),
|
Disconnect(DisconnectReason),
|
||||||
/// Send a popup notification such as "Waypoint Saved"
|
/// Send a popup notification such as "Waypoint Saved"
|
||||||
Notification(Notification),
|
Notification(Notification),
|
||||||
|
UpdatePendingTrade(usize, PendingTrade),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerGeneral {
|
impl ServerGeneral {
|
||||||
@ -227,7 +229,8 @@ impl ServerMsg {
|
|||||||
| ServerGeneral::TerrainBlockUpdates(_)
|
| ServerGeneral::TerrainBlockUpdates(_)
|
||||||
| ServerGeneral::SetViewDistance(_)
|
| ServerGeneral::SetViewDistance(_)
|
||||||
| ServerGeneral::Outcomes(_)
|
| ServerGeneral::Outcomes(_)
|
||||||
| ServerGeneral::Knockback(_) => {
|
| ServerGeneral::Knockback(_)
|
||||||
|
| ServerGeneral::UpdatePendingTrade(_, _) => {
|
||||||
c_type == ClientType::Game && presence.is_some()
|
c_type == ClientType::Game && presence.is_some()
|
||||||
},
|
},
|
||||||
// Always possible
|
// Always possible
|
||||||
|
@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use specs::{Component, DerefFlaggedStorage};
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use hashbrown::HashMap;
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// Default duration before an input is considered 'held'.
|
/// Default duration before an input is considered 'held'.
|
||||||
@ -320,25 +319,3 @@ pub struct Mounting(pub Uid);
|
|||||||
impl Component for Mounting {
|
impl Component for Mounting {
|
||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A PendingTrade is associated with the entity that initiated the trade.
|
|
||||||
///
|
|
||||||
/// Items are not removed from the inventory during a PendingTrade: all the items are moved
|
|
||||||
/// atomically (if there's space and both parties agree) upon completion
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct PendingTrade {
|
|
||||||
/// `counterparty` is the other entity that's being traded with
|
|
||||||
counterparty: Uid,
|
|
||||||
/// `self_offer` represents the items and quantities of the initiator's items being offered
|
|
||||||
self_offer: HashMap<InvSlotId, usize>,
|
|
||||||
/// `other_offer` represents the items and quantities of the counterparty's items being offered
|
|
||||||
other_offer: HashMap<InvSlotId, usize>,
|
|
||||||
/// `locked` is set when going from the first (mutable) screen to the second (readonly, review)
|
|
||||||
/// screen
|
|
||||||
locked: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for PendingTrade {
|
|
||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ pub use chat::{
|
|||||||
};
|
};
|
||||||
pub use controller::{
|
pub use controller::{
|
||||||
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, Input,
|
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, Input,
|
||||||
InventoryManip, LoadoutManip, MountState, Mounting, PendingTrade, SlotManip,
|
InventoryManip, LoadoutManip, MountState, Mounting, SlotManip,
|
||||||
};
|
};
|
||||||
pub use energy::{Energy, EnergyChange, EnergySource};
|
pub use energy::{Energy, EnergyChange, EnergySource};
|
||||||
pub use group::Group;
|
pub use group::Group;
|
||||||
|
@ -49,6 +49,7 @@ pub mod states;
|
|||||||
pub mod store;
|
pub mod store;
|
||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
pub mod trade;
|
||||||
pub mod typed;
|
pub mod typed;
|
||||||
pub mod uid;
|
pub mod uid;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
138
common/src/trade.rs
Normal file
138
common/src/trade.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use crate::{comp::inventory::slot::InvSlotId, uid::Uid};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
/// Clients submit `TradeActionMsg` to the server, which adds the Uid of the
|
||||||
|
/// player out-of-band (i.e. without trusting the client to say who it's
|
||||||
|
/// accepting on behalf of)
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum TradeActionMsg {
|
||||||
|
AddItem { item: InvSlotId, quantity: usize },
|
||||||
|
RemoveItem { item: InvSlotId, quantity: usize },
|
||||||
|
Phase1Accept,
|
||||||
|
Phase2Accept,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Items are not removed from the inventory during a PendingTrade: all the
|
||||||
|
/// items are moved atomically (if there's space and both parties agree) upon
|
||||||
|
/// completion
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct PendingTrade {
|
||||||
|
/// `parties[0]` is the entity that initiated the trade, parties[1] is the
|
||||||
|
/// other entity that's being traded with
|
||||||
|
pub parties: [Uid; 2],
|
||||||
|
/// `offers[i]` represents the items and quantities of the party i's items
|
||||||
|
/// being offered
|
||||||
|
pub offers: [HashMap<InvSlotId, usize>; 2],
|
||||||
|
/// phase1_accepts indicate that the parties wish to proceed to review
|
||||||
|
pub phase1_accepts: [bool; 2],
|
||||||
|
/// phase2_accepts indicate that the parties have reviewed the trade and
|
||||||
|
/// wish to commit it
|
||||||
|
pub phase2_accepts: [bool; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingTrade {
|
||||||
|
pub fn new(party: Uid, counterparty: Uid) -> PendingTrade {
|
||||||
|
PendingTrade {
|
||||||
|
parties: [party, counterparty],
|
||||||
|
offers: [HashMap::new(), HashMap::new()],
|
||||||
|
phase1_accepts: [false, false],
|
||||||
|
phase2_accepts: [false, false],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn in_phase1(&self) -> bool { !self.phase1_accepts[0] || !self.phase1_accepts[1] }
|
||||||
|
|
||||||
|
pub fn in_phase2(&self) -> bool {
|
||||||
|
(self.phase1_accepts[0] && self.phase1_accepts[1]) && (!self.phase2_accepts[0] || !self.phase2_accepts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_commit(&self) -> bool {
|
||||||
|
self.phase1_accepts[0] && self.phase1_accepts[1] && self.phase2_accepts[0] && self.phase2_accepts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn which_party(&self, party: Uid) -> Option<usize> {
|
||||||
|
self.parties
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, x)| **x == party)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_msg(&mut self, who: usize, msg: TradeActionMsg) {
|
||||||
|
use TradeActionMsg::*;
|
||||||
|
match msg {
|
||||||
|
AddItem { item, quantity } => {
|
||||||
|
if self.in_phase1() {
|
||||||
|
let total = self.offers[who].entry(item).or_insert(0);
|
||||||
|
*total = total.saturating_add(quantity);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RemoveItem { item, quantity } => {
|
||||||
|
if self.in_phase1() {
|
||||||
|
let total = self.offers[who].entry(item).or_insert(0);
|
||||||
|
*total = total.saturating_sub(quantity);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Phase1Accept => {
|
||||||
|
if self.in_phase1() {
|
||||||
|
self.phase1_accepts[who] = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Phase2Accept => {
|
||||||
|
if self.in_phase2() {
|
||||||
|
self.phase2_accepts[who] = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Trades {
|
||||||
|
pub next_id: usize,
|
||||||
|
pub trades: HashMap<usize, PendingTrade>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trades {
|
||||||
|
pub fn begin_trade(&mut self, party: Uid, counterparty: Uid) -> usize {
|
||||||
|
let id = self.next_id;
|
||||||
|
self.next_id = id.wrapping_add(1);
|
||||||
|
self.trades
|
||||||
|
.insert(id, PendingTrade::new(party, counterparty));
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_trade_action(&mut self, id: usize, who: Uid, msg: TradeActionMsg) {
|
||||||
|
if let Some(trade) = self.trades.get_mut(&id) {
|
||||||
|
if let Some(party) = trade.which_party(who) {
|
||||||
|
trade.process_msg(party, msg);
|
||||||
|
} else {
|
||||||
|
warn!("An entity who is not a party to trade {} tried to modify it", id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Attempt to modify nonexistent trade id {}", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decline_trade(&mut self, id: usize, who: Uid) {
|
||||||
|
if let Some(trade) = self.trades.remove(&id) {
|
||||||
|
if let None = trade.which_party(who) {
|
||||||
|
warn!("An entity who is not a party to trade {} tried to decline it", id);
|
||||||
|
// put it back
|
||||||
|
self.trades.insert(id, trade);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Attempt to decline nonexistent trade id {}", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Trades {
|
||||||
|
fn default() -> Trades {
|
||||||
|
Trades {
|
||||||
|
next_id: 0,
|
||||||
|
trades: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -102,7 +102,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let Some(counterparty_entity) =
|
if let Some(counterparty_entity) =
|
||||||
uid_allocator.retrieve_entity_internal(counterparty_uid.id())
|
uid_allocator.retrieve_entity_internal(counterparty_uid.id())
|
||||||
{
|
{
|
||||||
server_emitter.emit(ServerEvent::InitiateTrade(entity, counterparty_entity));
|
server_emitter
|
||||||
|
.emit(ServerEvent::InitiateTrade(entity, counterparty_entity));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ControlEvent::InventoryManip(manip) => {
|
ControlEvent::InventoryManip(manip) => {
|
||||||
|
@ -10,6 +10,7 @@ use common::{
|
|||||||
span,
|
span,
|
||||||
terrain::{Block, TerrainChunk, TerrainGrid},
|
terrain::{Block, TerrainChunk, TerrainGrid},
|
||||||
time::DayPeriod,
|
time::DayPeriod,
|
||||||
|
trade::Trades,
|
||||||
vol::{ReadVol, WriteVol},
|
vol::{ReadVol, WriteVol},
|
||||||
};
|
};
|
||||||
use common_net::sync::WorldSyncExt;
|
use common_net::sync::WorldSyncExt;
|
||||||
@ -190,6 +191,7 @@ impl State {
|
|||||||
ecs.insert(RegionMap::new());
|
ecs.insert(RegionMap::new());
|
||||||
ecs.insert(SysMetrics::default());
|
ecs.insert(SysMetrics::default());
|
||||||
ecs.insert(PhysicsMetrics::default());
|
ecs.insert(PhysicsMetrics::default());
|
||||||
|
ecs.insert(Trades::default());
|
||||||
|
|
||||||
// Load plugins from asset directory
|
// Load plugins from asset directory
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
|
@ -88,7 +88,8 @@ impl Client {
|
|||||||
| ServerGeneral::TerrainBlockUpdates(_)
|
| ServerGeneral::TerrainBlockUpdates(_)
|
||||||
| ServerGeneral::SetViewDistance(_)
|
| ServerGeneral::SetViewDistance(_)
|
||||||
| ServerGeneral::Outcomes(_)
|
| ServerGeneral::Outcomes(_)
|
||||||
| ServerGeneral::Knockback(_) => {
|
| ServerGeneral::Knockback(_)
|
||||||
|
| ServerGeneral::UpdatePendingTrade(_, _) => {
|
||||||
self.in_game_stream.try_lock().unwrap().send(g)
|
self.in_game_stream.try_lock().unwrap().send(g)
|
||||||
},
|
},
|
||||||
// Always possible
|
// Always possible
|
||||||
@ -166,7 +167,10 @@ impl Client {
|
|||||||
| ServerGeneral::TerrainBlockUpdates(_)
|
| ServerGeneral::TerrainBlockUpdates(_)
|
||||||
| ServerGeneral::SetViewDistance(_)
|
| ServerGeneral::SetViewDistance(_)
|
||||||
| ServerGeneral::Outcomes(_)
|
| ServerGeneral::Outcomes(_)
|
||||||
| ServerGeneral::Knockback(_) => PreparedMsg::new(2, &g, &self.in_game_stream),
|
| ServerGeneral::Knockback(_)
|
||||||
|
| ServerGeneral::UpdatePendingTrade(_, _) => {
|
||||||
|
PreparedMsg::new(2, &g, &self.in_game_stream)
|
||||||
|
},
|
||||||
// Always possible
|
// Always possible
|
||||||
ServerGeneral::PlayerListUpdate(_)
|
ServerGeneral::PlayerListUpdate(_)
|
||||||
| ServerGeneral::ChatMsg(_)
|
| ServerGeneral::ChatMsg(_)
|
||||||
|
@ -5,6 +5,7 @@ use common::{
|
|||||||
group::{self, Group, GroupManager, Invite, InviteKind, PendingInvites},
|
group::{self, Group, GroupManager, Invite, InviteKind, PendingInvites},
|
||||||
ChatType, GroupManip,
|
ChatType, GroupManip,
|
||||||
},
|
},
|
||||||
|
trade::Trades,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use common_net::{
|
use common_net::{
|
||||||
@ -20,7 +21,12 @@ const INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(31);
|
|||||||
/// Reduced duration shown to the client to help alleviate latency issues
|
/// Reduced duration shown to the client to help alleviate latency issues
|
||||||
const PRESENTED_INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(30);
|
const PRESENTED_INVITE_TIMEOUT_DUR: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
pub fn handle_invite(server: &mut Server, inviter: specs::Entity, invitee_uid: Uid, kind: InviteKind) {
|
pub fn handle_invite(
|
||||||
|
server: &mut Server,
|
||||||
|
inviter: specs::Entity,
|
||||||
|
invitee_uid: Uid,
|
||||||
|
kind: InviteKind,
|
||||||
|
) {
|
||||||
let max_group_size = server.settings().max_player_group_size;
|
let max_group_size = server.settings().max_player_group_size;
|
||||||
let state = server.state_mut();
|
let state = server.state_mut();
|
||||||
let clients = state.ecs().read_storage::<Client>();
|
let clients = state.ecs().read_storage::<Client>();
|
||||||
@ -129,10 +135,11 @@ pub fn handle_invite(server: &mut Server, inviter: specs::Entity, invitee_uid: U
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match pending_invites.entry(inviter) {
|
match pending_invites.entry(inviter) {
|
||||||
Ok(entry) => {
|
Ok(entry) => {
|
||||||
entry
|
entry.or_insert_with(|| PendingInvites(Vec::new())).0.push((
|
||||||
.or_insert_with(|| PendingInvites(Vec::new()))
|
invitee,
|
||||||
.0
|
kind,
|
||||||
.push((invitee, kind, Instant::now() + INVITE_TIMEOUT_DUR));
|
Instant::now() + INVITE_TIMEOUT_DUR,
|
||||||
|
));
|
||||||
invite_sent = true;
|
invite_sent = true;
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
@ -151,8 +158,7 @@ pub fn handle_invite(server: &mut Server, inviter: specs::Entity, invitee_uid: U
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If client comp
|
// If client comp
|
||||||
if let (Some(client), Some(inviter)) = (clients.get(invitee), uids.get(inviter).copied())
|
if let (Some(client), Some(inviter)) = (clients.get(invitee), uids.get(inviter).copied()) {
|
||||||
{
|
|
||||||
if send_invite() {
|
if send_invite() {
|
||||||
client.send_fallible(ServerGeneral::GroupInvite {
|
client.send_fallible(ServerGeneral::GroupInvite {
|
||||||
inviter,
|
inviter,
|
||||||
@ -211,26 +217,37 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
kind,
|
kind,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let InviteKind::Group = kind {
|
match kind {
|
||||||
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
InviteKind::Group => {
|
||||||
group_manager.add_group_member(
|
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||||
inviter,
|
group_manager.add_group_member(
|
||||||
entity,
|
inviter,
|
||||||
&state.ecs().entities(),
|
entity,
|
||||||
&mut state.ecs().write_storage(),
|
&state.ecs().entities(),
|
||||||
&state.ecs().read_storage(),
|
&mut state.ecs().write_storage(),
|
||||||
&uids,
|
&state.ecs().read_storage(),
|
||||||
|entity, group_change| {
|
&uids,
|
||||||
clients
|
|entity, group_change| {
|
||||||
.get(entity)
|
clients
|
||||||
.and_then(|c| {
|
.get(entity)
|
||||||
group_change
|
.and_then(|c| {
|
||||||
.try_map(|e| uids.get(e).copied())
|
group_change
|
||||||
.map(|g| (g, c))
|
.try_map(|e| uids.get(e).copied())
|
||||||
})
|
.map(|g| (g, c))
|
||||||
.map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g)));
|
})
|
||||||
},
|
.map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g)));
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
InviteKind::Trade => {
|
||||||
|
if let (Some(inviter_uid), Some(invitee_uid)) = (uids.get(inviter).copied(), uids.get(entity).copied()) {
|
||||||
|
let mut trades = state.ecs().write_resource::<Trades>();
|
||||||
|
let id = trades.begin_trade(inviter_uid, invitee_uid);
|
||||||
|
let trade = trades.trades[&id].clone();
|
||||||
|
clients.get(inviter).map(|c| c.send(ServerGeneral::UpdatePendingTrade(id, trade.clone())));
|
||||||
|
clients.get(entity).map(|c| c.send(ServerGeneral::UpdatePendingTrade(id, trade)));
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,10 @@ use specs::{world::WorldExt, Entity as EcsEntity};
|
|||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, agent::AgentEvent, group::InviteKind, inventory::slot::EquipSlot, item, slot::Slot, Inventory, Pos},
|
comp::{
|
||||||
|
self, agent::AgentEvent, group::InviteKind, inventory::slot::EquipSlot, item, slot::Slot,
|
||||||
|
Inventory, Pos,
|
||||||
|
},
|
||||||
consts::MAX_MOUNT_RANGE,
|
consts::MAX_MOUNT_RANGE,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,8 @@ use entity_manipulation::{
|
|||||||
};
|
};
|
||||||
use group_manip::handle_group;
|
use group_manip::handle_group;
|
||||||
use interaction::{
|
use interaction::{
|
||||||
handle_lantern, handle_initiate_trade, handle_mount, handle_npc_interaction, handle_possess, handle_unmount,
|
handle_initiate_trade, handle_lantern, handle_mount, handle_npc_interaction, handle_possess,
|
||||||
|
handle_unmount,
|
||||||
};
|
};
|
||||||
use inventory_manip::handle_inventory;
|
use inventory_manip::handle_inventory;
|
||||||
use player::{handle_client_disconnect, handle_exit_ingame};
|
use player::{handle_client_disconnect, handle_exit_ingame};
|
||||||
|
@ -16,7 +16,10 @@ use crate::{
|
|||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{
|
||||||
combat,
|
combat,
|
||||||
comp::{group::{InviteKind, Role}, BuffKind, Stats},
|
comp::{
|
||||||
|
group::{InviteKind, Role},
|
||||||
|
BuffKind, Stats,
|
||||||
|
},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
use common_net::sync::WorldSyncExt;
|
use common_net::sync::WorldSyncExt;
|
||||||
@ -799,18 +802,14 @@ impl<'a> Widget for Group<'a> {
|
|||||||
|
|
||||||
let name = uid_to_name_text(invite_uid, &self.client);
|
let name = uid_to_name_text(invite_uid, &self.client);
|
||||||
let invite_text = match kind {
|
let invite_text = match kind {
|
||||||
InviteKind::Group => {
|
InviteKind::Group => self
|
||||||
self
|
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.group.invite_to_join")
|
.get("hud.group.invite_to_join")
|
||||||
.replace("{name}", &name)
|
.replace("{name}", &name),
|
||||||
},
|
InviteKind::Trade => self
|
||||||
InviteKind::Trade => {
|
|
||||||
self
|
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.group.invite_to_trade")
|
.get("hud.group.invite_to_trade")
|
||||||
.replace("{name}", &name)
|
.replace("{name}", &name),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
Text::new(&invite_text)
|
Text::new(&invite_text)
|
||||||
.mid_top_with_margin_on(state.ids.bg, 5.0)
|
.mid_top_with_margin_on(state.ids.bg, 5.0)
|
||||||
|
@ -9,7 +9,9 @@ use client::{self, Client};
|
|||||||
use common::{
|
use common::{
|
||||||
assets::AssetExt,
|
assets::AssetExt,
|
||||||
comp,
|
comp,
|
||||||
comp::{inventory::slot::Slot, group::InviteKind, ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
|
comp::{
|
||||||
|
group::InviteKind, inventory::slot::Slot, ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel,
|
||||||
|
},
|
||||||
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
span,
|
span,
|
||||||
@ -121,7 +123,11 @@ impl SessionState {
|
|||||||
client::Event::Chat(m) => {
|
client::Event::Chat(m) => {
|
||||||
self.hud.new_message(m);
|
self.hud.new_message(m);
|
||||||
},
|
},
|
||||||
client::Event::InviteComplete { target, answer, kind } => {
|
client::Event::InviteComplete {
|
||||||
|
target,
|
||||||
|
answer,
|
||||||
|
kind,
|
||||||
|
} => {
|
||||||
// TODO: i18n
|
// TODO: i18n
|
||||||
let kind_str = match kind {
|
let kind_str = match kind {
|
||||||
InviteKind::Group => "Group",
|
InviteKind::Group => "Group",
|
||||||
@ -138,7 +144,7 @@ impl SessionState {
|
|||||||
};
|
};
|
||||||
let msg = format!("{} invite to {} {}", kind_str, target_name, answer_str);
|
let msg = format!("{} invite to {} {}", kind_str, target_name, answer_str);
|
||||||
self.hud.new_message(ChatType::Meta.chat_msg(msg));
|
self.hud.new_message(ChatType::Meta.chat_msg(msg));
|
||||||
}
|
},
|
||||||
client::Event::InventoryUpdated(inv_event) => {
|
client::Event::InventoryUpdated(inv_event) => {
|
||||||
let sfx_triggers = self.scene.sfx_mgr.triggers.read();
|
let sfx_triggers = self.scene.sfx_mgr.triggers.read();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user