Slow down when urges are high.
Also fix a few issues with concurrency errors from the DB.
This commit is contained in:
parent
daa847b448
commit
2beaf0d2af
@ -21,6 +21,7 @@ use std::collections::BTreeSet;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio_postgres::error::{DbError, SqlState};
|
||||||
use tokio_postgres::types::ToSql;
|
use tokio_postgres::types::ToSql;
|
||||||
use tokio_postgres::NoTls;
|
use tokio_postgres::NoTls;
|
||||||
use tokio_postgres::{config::Config as PgConfig, row::Row};
|
use tokio_postgres::{config::Config as PgConfig, row::Row};
|
||||||
@ -1810,3 +1811,18 @@ impl DBTrans {
|
|||||||
.ok_or("Transaction already closed".into())
|
.ok_or("Transaction already closed".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_concurrency_error(e: &(dyn Error + 'static)) -> bool {
|
||||||
|
match e.source() {
|
||||||
|
None => {}
|
||||||
|
Some(e_src) => {
|
||||||
|
if is_concurrency_error(e_src) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match e.downcast_ref::<DbError>() {
|
||||||
|
None => false,
|
||||||
|
Some(dbe) => dbe.code() == &SqlState::T_R_SERIALIZATION_FAILURE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,40 +1,41 @@
|
|||||||
use tokio::{task, time};
|
use crate::DResult;
|
||||||
use tokio::net::{TcpSocket, TcpStream, lookup_host};
|
|
||||||
use log::{info, warn};
|
|
||||||
use tokio_util::codec;
|
|
||||||
use tokio_util::codec::length_delimited::LengthDelimitedCodec;
|
|
||||||
use tokio_serde::formats::Cbor;
|
|
||||||
use blastmud_interfaces::*;
|
use blastmud_interfaces::*;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use tokio::sync::{Mutex, mpsc, oneshot};
|
use log::{info, warn};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use crate::DResult;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use tokio::net::{lookup_host, TcpSocket, TcpStream};
|
||||||
|
use tokio::sync::{mpsc, oneshot, Mutex};
|
||||||
|
use tokio::{task, time};
|
||||||
|
use tokio_serde::formats::Cbor;
|
||||||
|
use tokio_util::codec;
|
||||||
|
use tokio_util::codec::length_delimited::LengthDelimitedCodec;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ListenerSend {
|
pub struct ListenerSend {
|
||||||
pub message: MessageToListener,
|
pub message: MessageToListener,
|
||||||
pub ack_notify: oneshot::Sender<()>
|
pub ack_notify: oneshot::Sender<()>,
|
||||||
}
|
}
|
||||||
pub type ListenerMap = Arc<Mutex<BTreeMap<Uuid, mpsc::Sender<ListenerSend>>>>;
|
pub type ListenerMap = Arc<Mutex<BTreeMap<Uuid, mpsc::Sender<ListenerSend>>>>;
|
||||||
|
|
||||||
async fn handle_from_listener<FHandler, HandlerFut>(
|
async fn handle_from_listener<FHandler, HandlerFut>(
|
||||||
conn: TcpStream,
|
conn: TcpStream,
|
||||||
message_handler: FHandler,
|
message_handler: FHandler,
|
||||||
listener_map: ListenerMap)
|
listener_map: ListenerMap,
|
||||||
where
|
) where
|
||||||
FHandler: Fn(Uuid, MessageFromListener) -> HandlerFut + Send + 'static,
|
FHandler: Fn(Uuid, MessageFromListener) -> HandlerFut + Send + 'static,
|
||||||
HandlerFut: Future<Output = DResult<()>> + Send + 'static {
|
HandlerFut: Future<Output = DResult<()>> + Send + 'static,
|
||||||
|
{
|
||||||
let mut conn_framed = tokio_serde::Framed::new(
|
let mut conn_framed = tokio_serde::Framed::new(
|
||||||
codec::Framed::new(conn, LengthDelimitedCodec::new()),
|
codec::Framed::new(conn, LengthDelimitedCodec::new()),
|
||||||
Cbor::<MessageFromListener, MessageToListener>::default()
|
Cbor::<MessageFromListener, MessageToListener>::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let listener_id = match conn_framed.try_next().await {
|
let listener_id = match conn_framed.try_next().await {
|
||||||
Ok(Some(ref msg@MessageFromListener::ListenerPing { uuid })) => {
|
Ok(Some(ref msg @ MessageFromListener::ListenerPing { uuid })) => {
|
||||||
let handle_fut = message_handler(uuid.clone(), msg.clone());
|
let handle_fut = message_handler(uuid.clone(), msg.clone());
|
||||||
match handle_fut.await {
|
match handle_fut.await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
@ -43,7 +44,7 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
uuid
|
uuid
|
||||||
},
|
}
|
||||||
Ok(Some(msg)) => {
|
Ok(Some(msg)) => {
|
||||||
warn!("Got non-ping first message from listener: {:?}", msg);
|
warn!("Got non-ping first message from listener: {:?}", msg);
|
||||||
return;
|
return;
|
||||||
@ -53,15 +54,24 @@ where
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Lost listener connection to error {} before first message", e);
|
warn!(
|
||||||
|
"Lost listener connection to error {} before first message",
|
||||||
|
e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match conn_framed.send(MessageToListener::AcknowledgeMessage).await {
|
match conn_framed
|
||||||
|
.send(MessageToListener::AcknowledgeMessage)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Got error sending listener acknowledge for initial ping: {}", e);
|
warn!(
|
||||||
|
"Got error sending listener acknowledge for initial ping: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,17 +199,20 @@ pub fn make_listener_map() -> ListenerMap {
|
|||||||
pub async fn start_listener<FHandler, HandlerFut>(
|
pub async fn start_listener<FHandler, HandlerFut>(
|
||||||
bind_to: String,
|
bind_to: String,
|
||||||
listener_map: ListenerMap,
|
listener_map: ListenerMap,
|
||||||
handle_message: FHandler
|
handle_message: FHandler,
|
||||||
) -> DResult<()>
|
) -> DResult<()>
|
||||||
where
|
where
|
||||||
FHandler: Fn(Uuid, MessageFromListener) -> HandlerFut + Send + Clone + 'static,
|
FHandler: Fn(Uuid, MessageFromListener) -> HandlerFut + Send + Clone + 'static,
|
||||||
HandlerFut: Future<Output = DResult<()>> + Send + 'static
|
HandlerFut: Future<Output = DResult<()>> + Send + 'static,
|
||||||
{
|
{
|
||||||
info!("Starting listener on {}", bind_to);
|
info!("Starting listener on {}", bind_to);
|
||||||
let addr = lookup_host(bind_to).await?.next().expect("listener address didn't resolve");
|
let addr = lookup_host(bind_to)
|
||||||
|
.await?
|
||||||
|
.next()
|
||||||
|
.expect("listener address didn't resolve");
|
||||||
let socket = match addr {
|
let socket = match addr {
|
||||||
SocketAddr::V4 {..} => TcpSocket::new_v4()?,
|
SocketAddr::V4 { .. } => TcpSocket::new_v4()?,
|
||||||
SocketAddr::V6 {..} => TcpSocket::new_v6()?
|
SocketAddr::V6 { .. } => TcpSocket::new_v6()?,
|
||||||
};
|
};
|
||||||
socket.set_reuseaddr(true)?;
|
socket.set_reuseaddr(true)?;
|
||||||
socket.set_reuseport(true)?;
|
socket.set_reuseport(true)?;
|
||||||
@ -215,7 +228,11 @@ where
|
|||||||
}
|
}
|
||||||
Ok((socket, _)) => {
|
Ok((socket, _)) => {
|
||||||
info!("Accepted new inbound connection from listener");
|
info!("Accepted new inbound connection from listener");
|
||||||
task::spawn(handle_from_listener(socket, handle_message.clone(), listener_map_for_task.clone()));
|
task::spawn(handle_from_listener(
|
||||||
|
socket,
|
||||||
|
handle_message.clone(),
|
||||||
|
listener_map_for_task.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,42 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
use std::fs;
|
|
||||||
use std::error::Error;
|
|
||||||
use log::{info, error, LevelFilter};
|
|
||||||
use simple_logger::SimpleLogger;
|
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
|
||||||
use db::DBPool;
|
use db::DBPool;
|
||||||
|
use log::{error, info, LevelFilter};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use simple_logger::SimpleLogger;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use tokio::signal::unix::{signal, SignalKind};
|
||||||
|
|
||||||
|
mod av;
|
||||||
mod db;
|
mod db;
|
||||||
|
mod language;
|
||||||
mod listener;
|
mod listener;
|
||||||
mod message_handler;
|
mod message_handler;
|
||||||
mod version_cutover;
|
|
||||||
mod av;
|
|
||||||
mod regular_tasks;
|
|
||||||
mod models;
|
mod models;
|
||||||
mod static_content;
|
mod regular_tasks;
|
||||||
mod language;
|
|
||||||
mod services;
|
mod services;
|
||||||
|
mod static_content;
|
||||||
|
mod version_cutover;
|
||||||
|
|
||||||
pub type DResult<T> = Result<T, Box<dyn Error + Send + Sync>>;
|
pub type DResult<T> = Result<T, Box<dyn Error + Send + Sync + 'static>>;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Config {
|
struct Config {
|
||||||
listener: String,
|
listener: String,
|
||||||
pidfile: String,
|
pidfile: String,
|
||||||
database_conn_string: String
|
database_conn_string: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_latest_config() -> DResult<Config> {
|
fn read_latest_config() -> DResult<Config> {
|
||||||
serde_yaml::from_str(&fs::read_to_string("gameserver.conf")?).
|
serde_yaml::from_str(&fs::read_to_string("gameserver.conf")?)
|
||||||
map_err(|error| Box::new(error) as Box<dyn Error + Send + Sync>)
|
.map_err(|error| Box::new(error) as Box<dyn Error + Send + Sync>)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(worker_threads = 2)]
|
#[tokio::main(worker_threads = 2)]
|
||||||
async fn main() -> DResult<()> {
|
async fn main() -> DResult<()> {
|
||||||
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
|
SimpleLogger::new()
|
||||||
|
.with_level(LevelFilter::Info)
|
||||||
|
.init()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
av::check().or_else(|e| -> Result<(), Box<dyn Error + Send + Sync>> {
|
av::check().or_else(|e| -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
error!("Couldn't verify age-verification.yml - this is not a complete game. Check README.md: {}", e);
|
error!("Couldn't verify age-verification.yml - this is not a complete game. Check README.md: {}", e);
|
||||||
@ -50,11 +53,12 @@ async fn main() -> DResult<()> {
|
|||||||
let listener_map = listener::make_listener_map();
|
let listener_map = listener::make_listener_map();
|
||||||
|
|
||||||
let mh_pool = pool.clone();
|
let mh_pool = pool.clone();
|
||||||
listener::start_listener(config.listener, listener_map.clone(),
|
listener::start_listener(
|
||||||
move |listener_id, msg| {
|
config.listener,
|
||||||
message_handler::handle(listener_id, msg, mh_pool.clone())
|
listener_map.clone(),
|
||||||
}
|
move |listener_id, msg| message_handler::handle(listener_id, msg, mh_pool.clone()),
|
||||||
).await?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
static_content::refresh_static_content(&pool).await?;
|
static_content::refresh_static_content(&pool).await?;
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use blastmud_interfaces::*;
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use MessageFromListener::*;
|
|
||||||
use uuid::Uuid;
|
|
||||||
use crate::DResult;
|
use crate::DResult;
|
||||||
|
use blastmud_interfaces::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use MessageFromListener::*;
|
||||||
|
|
||||||
mod new_session;
|
mod new_session;
|
||||||
pub mod user_commands;
|
pub mod user_commands;
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ListenerSession {
|
pub struct ListenerSession {
|
||||||
pub listener: Uuid,
|
pub listener: Uuid,
|
||||||
pub session: Uuid
|
pub session: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -19,21 +19,22 @@ impl Default for ListenerSession {
|
|||||||
use uuid::uuid;
|
use uuid::uuid;
|
||||||
ListenerSession {
|
ListenerSession {
|
||||||
listener: uuid!("6f9c9b61-9228-4427-abd7-c4aef127a862"),
|
listener: uuid!("6f9c9b61-9228-4427-abd7-c4aef127a862"),
|
||||||
session: uuid!("668efb68-79d3-4004-9d6a-1e5757792e1a")
|
session: uuid!("668efb68-79d3-4004-9d6a-1e5757792e1a"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle(listener: Uuid, msg: MessageFromListener, pool: db::DBPool)
|
pub async fn handle(listener: Uuid, msg: MessageFromListener, pool: db::DBPool) -> DResult<()> {
|
||||||
-> DResult<()> {
|
|
||||||
match msg {
|
match msg {
|
||||||
ListenerPing { .. } => { pool.record_listener_ping(listener).await?; }
|
ListenerPing { .. } => {
|
||||||
|
pool.record_listener_ping(listener).await?;
|
||||||
|
}
|
||||||
SessionConnected { session, source } => {
|
SessionConnected { session, source } => {
|
||||||
new_session::handle(
|
new_session::handle(&ListenerSession { listener, session }, source, &pool).await?;
|
||||||
&ListenerSession { listener, session }, source, &pool).await?;
|
|
||||||
}
|
}
|
||||||
SessionDisconnected { session } => {
|
SessionDisconnected { session } => {
|
||||||
pool.end_session(ListenerSession { listener, session }).await?;
|
pool.end_session(ListenerSession { listener, session })
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
SessionSentLine { session, msg } => {
|
SessionSentLine { session, msg } => {
|
||||||
user_commands::handle(&ListenerSession { listener, session }, &msg, &pool).await?;
|
user_commands::handle(&ListenerSession { listener, session }, &msg, &pool).await?;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::ListenerSession;
|
use super::ListenerSession;
|
||||||
#[double]
|
#[double]
|
||||||
use crate::db::DBTrans;
|
use crate::db::DBTrans;
|
||||||
use crate::db::{DBPool, ItemSearchParams};
|
use crate::db::{is_concurrency_error, DBPool, ItemSearchParams};
|
||||||
use crate::models::user::UserFlag;
|
use crate::models::user::UserFlag;
|
||||||
use crate::models::{item::Item, session::Session, user::User};
|
use crate::models::{item::Item, session::Session, user::User};
|
||||||
use crate::DResult;
|
use crate::DResult;
|
||||||
@ -272,10 +272,13 @@ fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef>
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(test))]
|
pub async fn handle_in_trans(
|
||||||
pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DResult<()> {
|
session: &ListenerSession,
|
||||||
|
msg: &str,
|
||||||
|
pool: &DBPool,
|
||||||
|
trans: DBTrans,
|
||||||
|
) -> DResult<()> {
|
||||||
let (cmd, params) = parsing::parse_command_name(msg);
|
let (cmd, params) = parsing::parse_command_name(msg);
|
||||||
let trans = pool.start_transaction().await?;
|
|
||||||
let (mut session_dat, mut user_dat) = match trans.get_session_user_model(session).await? {
|
let (mut session_dat, mut user_dat) = match trans.get_session_user_model(session).await? {
|
||||||
None => {
|
None => {
|
||||||
// If the session has been cleaned up from the database, there is
|
// If the session has been cleaned up from the database, there is
|
||||||
@ -320,6 +323,24 @@ pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DRes
|
|||||||
Err(SystemError(e)) => Err(e)?,
|
Err(SystemError(e)) => Err(e)?,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DResult<()> {
|
||||||
|
loop {
|
||||||
|
let trans = pool.start_transaction().await?;
|
||||||
|
match handle_in_trans(session, msg, pool, trans).await {
|
||||||
|
Ok(_) => break,
|
||||||
|
Err(e) => {
|
||||||
|
if is_concurrency_error(e.as_ref()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pool.bump_session_time(&session).await?;
|
pool.bump_session_time(&session).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -691,7 +691,65 @@ impl QueueCommandHandler for QueueHandler {
|
|||||||
move_to_where(&use_location, direction, ctx).await?;
|
move_to_where(&use_location, direction, ctx).await?;
|
||||||
stand_if_needed(&ctx.trans, &mut ctx.item).await?;
|
stand_if_needed(&ctx.trans, &mut ctx.item).await?;
|
||||||
propagate_move_to_followers(&ctx.trans, &mut ctx.item, &direction, &source).await?;
|
propagate_move_to_followers(&ctx.trans, &mut ctx.item, &direction, &source).await?;
|
||||||
Ok(time::Duration::from_secs(1))
|
|
||||||
|
let mut move_factor: u64 = 1;
|
||||||
|
let mut slow_factors: Vec<String> = vec![];
|
||||||
|
if let Some(urges) = ctx.item.urges.as_ref() {
|
||||||
|
if urges.hunger.value > 9500 {
|
||||||
|
slow_factors.push("you're starving".to_owned());
|
||||||
|
move_factor *= 8;
|
||||||
|
} else if urges.hunger.value > 8000 {
|
||||||
|
slow_factors.push("you're very hungry".to_owned());
|
||||||
|
move_factor *= 4;
|
||||||
|
} else if urges.hunger.value > 5000 {
|
||||||
|
slow_factors.push("you're hungry".to_owned());
|
||||||
|
move_factor *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if urges.thirst.value > 9500 {
|
||||||
|
slow_factors.push("your throat is parched with thirst".to_owned());
|
||||||
|
move_factor *= 8;
|
||||||
|
} else if urges.thirst.value > 8000 {
|
||||||
|
slow_factors.push("you're very thirsty".to_owned());
|
||||||
|
move_factor *= 4;
|
||||||
|
} else if urges.thirst.value > 5000 {
|
||||||
|
slow_factors.push("you're thirsty".to_owned());
|
||||||
|
move_factor *= 2;
|
||||||
|
}
|
||||||
|
if urges.stress.value > 9500 {
|
||||||
|
slow_factors.push("you're exhausted".to_owned());
|
||||||
|
move_factor *= 8;
|
||||||
|
} else if urges.stress.value > 8000 {
|
||||||
|
slow_factors.push("you're very stressed and tired".to_owned());
|
||||||
|
move_factor *= 4;
|
||||||
|
} else if urges.stress.value > 5000 {
|
||||||
|
slow_factors.push("you're stressed and tired".to_owned());
|
||||||
|
move_factor *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if slow_factors.len() > 0 {
|
||||||
|
if let Some((sess, _)) = ctx.get_session().await? {
|
||||||
|
ctx.trans
|
||||||
|
.queue_for_session(
|
||||||
|
&sess,
|
||||||
|
Some(&format!(
|
||||||
|
"You move slowly because {}.\n",
|
||||||
|
language::join_words(
|
||||||
|
&slow_factors
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.as_str())
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if move_factor > 16 {
|
||||||
|
move_factor = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(time::Duration::from_secs(move_factor))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
pub mod session;
|
|
||||||
pub mod user;
|
|
||||||
pub mod item;
|
|
||||||
pub mod task;
|
|
||||||
pub mod consent;
|
pub mod consent;
|
||||||
pub mod corp;
|
pub mod corp;
|
||||||
|
pub mod item;
|
||||||
pub mod journal;
|
pub mod journal;
|
||||||
|
pub mod session;
|
||||||
|
pub mod task;
|
||||||
|
pub mod user;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||||
pub enum ConsentType {
|
pub enum ConsentType {
|
||||||
@ -8,7 +8,7 @@ pub enum ConsentType {
|
|||||||
Medicine,
|
Medicine,
|
||||||
Gifts,
|
Gifts,
|
||||||
Visit,
|
Visit,
|
||||||
Sex
|
Sex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConsentType {
|
impl ConsentType {
|
||||||
@ -20,7 +20,7 @@ impl ConsentType {
|
|||||||
"gifts" => Some(Gifts),
|
"gifts" => Some(Gifts),
|
||||||
"visit" => Some(Visit),
|
"visit" => Some(Visit),
|
||||||
"sex" => Some(Sex),
|
"sex" => Some(Sex),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,8 +38,8 @@ impl ConsentType {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
|
||||||
pub enum ConsentStatus {
|
pub enum ConsentStatus {
|
||||||
PendingAdd, // Added but awaiting other party to ratify by giving matching consent.
|
PendingAdd, // Added but awaiting other party to ratify by giving matching consent.
|
||||||
Active, // Consent in force, no delete pending.
|
Active, // Consent in force, no delete pending.
|
||||||
PendingDelete, // Pending cancellation but other party has to also disallow to ratify.
|
PendingDelete, // Pending cancellation but other party has to also disallow to ratify.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ impl Default for FightConsent {
|
|||||||
status: ConsentStatus::PendingAdd,
|
status: ConsentStatus::PendingAdd,
|
||||||
pending_change: None,
|
pending_change: None,
|
||||||
allow_pick: false,
|
allow_pick: false,
|
||||||
freely_revoke: false
|
freely_revoke: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,31 +76,30 @@ impl Default for Consent {
|
|||||||
Self {
|
Self {
|
||||||
fight_consent: None,
|
fight_consent: None,
|
||||||
expires: None,
|
expires: None,
|
||||||
only_in: vec!(),
|
only_in: vec![],
|
||||||
allow_private: false,
|
allow_private: false,
|
||||||
until_death: false
|
until_death: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Consent {
|
impl Consent {
|
||||||
pub fn to_str(&self) -> String {
|
pub fn to_str(&self) -> String {
|
||||||
let mut details = vec!();
|
let mut details = vec![];
|
||||||
if let Some(ref fc) = self.fight_consent {
|
if let Some(ref fc) = self.fight_consent {
|
||||||
match fc.status {
|
match fc.status {
|
||||||
ConsentStatus::PendingAdd => {
|
ConsentStatus::PendingAdd => {
|
||||||
details.push("pending acceptance".to_owned());
|
details.push("pending acceptance".to_owned());
|
||||||
},
|
}
|
||||||
ConsentStatus::PendingDelete => {
|
ConsentStatus::PendingDelete => {
|
||||||
details.push("pending agreement to delete".to_owned());
|
details.push("pending agreement to delete".to_owned());
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
match fc.pending_change.as_ref() {
|
match fc.pending_change.as_ref() {
|
||||||
None => {},
|
None => {}
|
||||||
Some(new_self) => {
|
Some(new_self) => {
|
||||||
details.push(format!("pending amendment to [{}]",
|
details.push(format!("pending amendment to [{}]", &new_self.to_str()));
|
||||||
&new_self.to_str()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fc.allow_pick {
|
if fc.allow_pick {
|
||||||
@ -122,10 +121,12 @@ impl Consent {
|
|||||||
details.push(format!("in {}", in_place))
|
details.push(format!("in {}", in_place))
|
||||||
}
|
}
|
||||||
if let Some(exp) = self.expires {
|
if let Some(exp) = self.expires {
|
||||||
details.push(format!("valid for {}",
|
details.push(format!(
|
||||||
humantime::format_duration(std::time::Duration::from_secs(
|
"valid for {}",
|
||||||
(exp - Utc::now()).num_seconds() as u64
|
humantime::format_duration(std::time::Duration::from_secs(
|
||||||
))));
|
(exp - Utc::now()).num_seconds() as u64
|
||||||
|
))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
return details.into_iter().join(", ");
|
return details.into_iter().join(", ");
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Clone)]
|
||||||
pub enum CorpPermission {
|
pub enum CorpPermission {
|
||||||
@ -22,7 +22,7 @@ impl CorpPermission {
|
|||||||
"war" => Some(War),
|
"war" => Some(War),
|
||||||
"config" | "configure" => Some(Configure),
|
"config" | "configure" => Some(Configure),
|
||||||
"finance" | "finances" => Some(Finance),
|
"finance" | "finances" => Some(Finance),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn display(&self) -> &'static str {
|
pub fn display(&self) -> &'static str {
|
||||||
@ -39,7 +39,6 @@ impl CorpPermission {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Clone)]
|
||||||
pub enum CorpCommType {
|
pub enum CorpCommType {
|
||||||
Chat,
|
Chat,
|
||||||
@ -60,7 +59,7 @@ impl CorpCommType {
|
|||||||
"reward" => Some(Reward),
|
"reward" => Some(Reward),
|
||||||
"death" => Some(Death),
|
"death" => Some(Death),
|
||||||
"consent" => Some(Consent),
|
"consent" => Some(Consent),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn display(&self) -> &'static str {
|
pub fn display(&self) -> &'static str {
|
||||||
@ -95,7 +94,7 @@ impl Default for Corp {
|
|||||||
Self {
|
Self {
|
||||||
name: "Unset".to_owned(),
|
name: "Unset".to_owned(),
|
||||||
allow_combat_required: false,
|
allow_combat_required: false,
|
||||||
member_permissions: vec!(),
|
member_permissions: vec![],
|
||||||
founded: Utc::now(),
|
founded: Utc::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,18 +118,11 @@ impl Default for CorpMembership {
|
|||||||
CorpMembership {
|
CorpMembership {
|
||||||
invited_at: None,
|
invited_at: None,
|
||||||
joined_at: None,
|
joined_at: None,
|
||||||
permissions: vec!(),
|
permissions: vec![],
|
||||||
allow_combat: false,
|
allow_combat: false,
|
||||||
job_title: "Employee".to_owned(),
|
job_title: "Employee".to_owned(),
|
||||||
priority: 100,
|
priority: 100,
|
||||||
comms_on: vec!(
|
comms_on: vec![Chat, Notice, Connect, Reward, Death, Consent],
|
||||||
Chat,
|
|
||||||
Notice,
|
|
||||||
Connect,
|
|
||||||
Reward,
|
|
||||||
Death,
|
|
||||||
Consent,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub enum JournalType {
|
pub enum JournalType {
|
||||||
SlayedMeanDog,
|
SlayedMeanDog,
|
||||||
Died
|
Died,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub enum JournalInProgress {
|
pub enum JournalInProgress {}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -22,7 +21,7 @@ impl Default for JournalState {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
completed_journals: BTreeSet::new(),
|
completed_journals: BTreeSet::new(),
|
||||||
in_progress_journals: vec!(),
|
in_progress_journals: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use crate::db::DBTrans;
|
|||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use crate::models::task::{TaskParse, TaskRecurrence};
|
use crate::models::task::{TaskParse, TaskRecurrence};
|
||||||
use crate::{
|
use crate::{
|
||||||
db,
|
db::{self, is_concurrency_error},
|
||||||
listener::{ListenerMap, ListenerSend},
|
listener::{ListenerMap, ListenerSend},
|
||||||
message_handler::user_commands::{delete, drop, hire, open, rent},
|
message_handler::user_commands::{delete, drop, hire, open, rent},
|
||||||
models::task::Task,
|
models::task::Task,
|
||||||
@ -168,6 +168,9 @@ async fn process_tasks_once(pool: db::DBPool) -> DResult<()> {
|
|||||||
};
|
};
|
||||||
match handler.do_task(&mut ctx).await {
|
match handler.do_task(&mut ctx).await {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
if is_concurrency_error(e.as_ref()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
task.meta.consecutive_failure_count += 1;
|
task.meta.consecutive_failure_count += 1;
|
||||||
warn!("Error handling event of type {} code {} (consecutive count: {}): {:?}",
|
warn!("Error handling event of type {} code {} (consecutive count: {}): {:?}",
|
||||||
&task.details.name(), &task.meta.task_code,
|
&task.details.name(), &task.meta.task_code,
|
||||||
@ -265,7 +268,7 @@ async fn process_tasks_once(pool: db::DBPool) -> DResult<()> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
async fn process_tasks_once(_pool: db::DBPool) -> DResult<()> {
|
async fn process_tasks_once(_pool: db::DBPool) -> DResult<()> {
|
||||||
task_handler_registry();
|
task_handler_registry();
|
||||||
unimplemented!();
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_task_runner(pool: db::DBPool) {
|
fn start_task_runner(pool: db::DBPool) {
|
||||||
|
@ -1,28 +1,35 @@
|
|||||||
|
use crate::DResult;
|
||||||
|
use log::info;
|
||||||
|
use nix::{
|
||||||
|
sys::signal::{kill, Signal},
|
||||||
|
unistd::{getpid, Pid},
|
||||||
|
};
|
||||||
|
use std::error::Error;
|
||||||
use std::fs::{read_to_string, write};
|
use std::fs::{read_to_string, write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::error::Error;
|
|
||||||
use log::info;
|
|
||||||
use nix::{sys::signal::{kill, Signal}, unistd::{Pid, getpid}};
|
|
||||||
use crate::DResult;
|
|
||||||
|
|
||||||
pub fn replace_old_gameserver(pidfile: &str) -> DResult<()> {
|
pub fn replace_old_gameserver(pidfile: &str) -> DResult<()> {
|
||||||
match read_to_string(pidfile) {
|
match read_to_string(pidfile) {
|
||||||
Err(e) =>
|
Err(e) => {
|
||||||
if e.kind() == std::io::ErrorKind::NotFound {
|
if e.kind() == std::io::ErrorKind::NotFound {
|
||||||
info!("pidfile not found, assuming not already running");
|
info!("pidfile not found, assuming not already running");
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
info!("Error reading pidfile (other than NotFound): {}", e);
|
info!("Error reading pidfile (other than NotFound): {}", e);
|
||||||
Err(Box::new(e) as Box::<dyn Error + Send + Sync>)
|
Err(Box::new(e) as Box<dyn Error + Send + Sync>)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
let pid: Pid = Pid::from_raw(f.parse().map_err(|e| Box::new(e) as Box::<dyn Error + Send + Sync>)?);
|
let pid: Pid = Pid::from_raw(
|
||||||
|
f.parse()
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)?,
|
||||||
|
);
|
||||||
if pid == getpid() {
|
if pid == getpid() {
|
||||||
info!("Pid in pidfile is me - ignoring");
|
info!("Pid in pidfile is me - ignoring");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
match read_to_string(format!("/proc/{}/cmdline", pid)) {
|
match read_to_string(format!("/proc/{}/cmdline", pid)) {
|
||||||
Ok(content) =>
|
Ok(content) => {
|
||||||
if content.contains("blastmud_game") {
|
if content.contains("blastmud_game") {
|
||||||
info!("pid in pidfile references blastmud_game; starting cutover");
|
info!("pid in pidfile references blastmud_game; starting cutover");
|
||||||
kill(pid, Signal::SIGUSR1)
|
kill(pid, Signal::SIGUSR1)
|
||||||
@ -31,6 +38,7 @@ pub fn replace_old_gameserver(pidfile: &str) -> DResult<()> {
|
|||||||
info!("Pid in pidfile is for process not including blastmud_game - ignoring pidfile");
|
info!("Pid in pidfile is for process not including blastmud_game - ignoring pidfile");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
info!("Pid in pidfile is gone - ignoring pidfile");
|
info!("Pid in pidfile is gone - ignoring pidfile");
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -40,5 +48,5 @@ pub fn replace_old_gameserver(pidfile: &str) -> DResult<()> {
|
|||||||
}?;
|
}?;
|
||||||
info!("Writing new pidfile");
|
info!("Writing new pidfile");
|
||||||
write(Path::new(pidfile), format!("{}", std::process::id()))
|
write(Path::new(pidfile), format!("{}", std::process::id()))
|
||||||
.map_err(|e| Box::new(e) as Box::<dyn Error + Send + Sync>)
|
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user