use crate::prelude::*;
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtCommand {
Noop,
SetSuperuser(bool),
SetRestrictedSshScope { key: sshkeys::KeySpec },
CreateAccount(AccountDetails),
UpdateAccount(AccountDetails),
DeleteAccount(AccountName),
ListAccounts { all: Option<bool> },
SelectAccount(AccountName), CheckAccount,
CreateGame {
game: InstanceName,
insns: Vec<MgmtGameInstruction>,
},
ListGames { all: Option<bool> },
AlterGame {
game: InstanceName,
insns: Vec<MgmtGameInstruction>,
how: MgmtGameUpdateMode,
},
DestroyGame {
game: InstanceName,
},
ClearBundles {
game: InstanceName,
},
UploadBundle {
game: InstanceName,
size: usize,
hash: bundles::Hash,
kind: bundles::Kind,
#[serde(default)] progress: ProgressUpdateMode,
},
ListBundles {
game: InstanceName,
},
DownloadBundle {
game: InstanceName,
id: bundles::Id,
},
LibraryListLibraries {
game: InstanceName,
},
LibraryListByGlob {
game: InstanceName,
lib: Option<String>,
pat: String,
},
SshListKeys,
SshAddKey { akl: sshkeys::AuthkeysLine },
SshDeleteKey { index: usize, id: sshkeys::Id },
ThisConnAuthBy, SshReinstallKeys,
LoadFakeRng(Vec<String>),
SetFakeTime(FakeTimeSpec),
}
#[derive(Debug,Clone,Hash,Eq,PartialEq)]
#[derive(Serialize,Deserialize)]
pub struct SshFingerprint(pub String);
#[derive(Debug,Serialize,Deserialize)]
pub struct AccountDetails {
pub account: AccountName,
pub nick: Option<String>,
pub timezone: Option<String>,
pub layout: Option<PresentationLayout>,
#[serde(flatten)]
pub access: Option<Box<dyn PlayerAccessSpec>>,
}
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtResponse {
Fine,
Progress(ProgressInfo<'static>),
Error { error: MgmtError },
AlterGame { error: Option<MgmtError>, responses: Vec<MgmtGameResponse> },
AccountsList(Vec<Arc<AccountName>>),
GamesList(Vec<Arc<InstanceName>>),
Libraries(Vec<LibraryEnquiryData>),
LibraryItems(Vec<ItemEnquiryData>),
Bundles { bundles: MgmtBundleList },
Bundle { bundle: bundles::Id },
SshKeys(Vec<sshkeys::MgmtKeyReport>),
SshKeyAdded { index: usize, id: sshkeys::Id },
ThisConnAuthBy(MgmtThisConnAuthBy),
}
pub type MgmtBundleList = BTreeMap<bundles::Id, bundles::State>;
#[derive(Debug,Clone,Serialize,Deserialize)]
pub enum MgmtThisConnAuthBy {
Local,
Ssh { key: sshkeys::KeySpec },
}
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtGameInstruction {
Noop,
Info,
SetTableSize(Pos),
SetTableColour(ColourSpec),
InsnMark(Box<[u8]>),
Synch, SynchLog,
PieceIdLookupFwd { piece: PieceId, player: PlayerId, },
PieceIdLookupRev { vpid: VisiblePieceId, player: PlayerId, },
ListPieces,
AddPieces(PiecesSpec),
DeletePiece(PieceId),
DeletePieceAlias(String),
DefinePieceAlias { alias: String, target: Box<dyn PieceSpec> },
ClearGame { },
ResetFromNamedSpec { spec: String },
ResetFromGameSpec { spec_toml: String },
ResetPlayerAccess(PlayerId),
RedeliverPlayerAccess(PlayerId),
JoinGame { details: MgmtPlayerDetails },
UpdatePlayer { player: PlayerId, details: MgmtPlayerDetails },
LeaveGame(PlayerId),
SetLinks(HashMap<LinkKind, UrlSpec>),
RemoveLink { kind: LinkKind },
SetLink { kind: LinkKind, url: UrlSpec },
ClearLog,
SetACL { acl: Acl<TablePermission> },
}
#[derive(Debug,Serialize,Deserialize)]
pub struct MgmtPlayerDetails {
pub nick: Option<String>,
}
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtGameResponse {
Fine,
Info(MgmtGameResponseGameInfo),
InsnMark(Box<[u8]>),
InsnExpanded,
Synch(Generation),
InternalPieceId(Option<PieceId>),
VisiblePieceId(Option<VisiblePieceId>),
Pieces {
pieces: Vec<MgmtGamePieceInfo>,
pcaliases: BTreeSet<String>,
},
JoinGame {
nick: String,
player: PlayerId,
token: AccessTokenReport,
},
PlayerAccessToken(AccessTokenReport),
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct AccessTokenInfo { pub url: String }
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct AccessTokenReport { pub lines: Vec<String> }
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct MgmtGameResponseGameInfo {
pub table_size: Pos,
pub players: SecondarySlotMap<PlayerId, MgmtPlayerInfo>,
pub links: Vec<(LinkKind, UrlSpec)>,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct MgmtPlayerInfo {
pub account: AccountName,
pub nick: String,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct MgmtGamePieceInfo {
pub piece: PieceId,
pub itemname: shapelib::GoodItemName,
#[serde(flatten)]
pub visible: Option<MgmtGamePieceVisibleInfo>,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct MgmtGamePieceVisibleInfo {
pub pos: Pos,
pub face: FaceId,
pub desc_html: Html,
pub bbox: Rect,
}
#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
pub enum MgmtGameUpdateMode {
Online,
Bulk,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Serialize,Deserialize)]
pub enum ProgressUpdateMode {
None,
Simplex,
Duplex,
}
impl Default for ProgressUpdateMode {
fn default() -> Self { PUM::None }
}
#[derive(Debug,Clone,Error,Serialize,Deserialize)]
pub enum MgmtError {
#[error("failed to parse protocol command: {0}")] CommandParseFailed(String),
#[error("not authorised")] AuthorisationError,
#[error("superuse authorisation requiredr")] SuperuserAuthorisationRequired,
#[error("command requires account specified")] SpecifyAccount,
#[error("command requires nick specified")] MustSpecifyNick,
#[error("object or item already exists")] AlreadyExists,
#[error("game already has player with that nick")] NickCollision,
#[error("{0}")] GameBeingDestroyed (#[from] GameBeingDestroyed),
#[error("game not found")] GameNotFound,
#[error("{0}")] AccountNotFound (#[from] AccountNotFound),
#[error("{0}")] PlayerNotFound (#[from] PlayerNotFound),
#[error("piece not found in game")] PieceNotFound,
#[error("limit exceeded")] LimitExceeded,
#[error("server failure: {0}")] ServerFailure(String),
#[error("{0}")] ZCoordinateOverflow (#[from] zcoord::Overflow),
#[error("bad glob pattern: {pat:?}: {msg}")]
BadGlob { pat: String, msg: String },
#[error("{0}")] BadSpec (#[from] SpecError),
#[error("access token delivery failed: {0}")]
TokenDeliveryFailed (#[from] TokenDeliveryError),
#[error("{0}")] CoordinateOverflow (#[from] CoordinateOverflow),
#[error("TOML syntax error: {0}")] TomlSyntaxError(String),
#[error("TOML structure error: {0}")] TomlStructureError(String),
#[error("{0}, command not supported")] RngIsReal(#[from] RngIsReal),
#[error("{0}, command not supported")] TimeIsReal(#[from] TimeIsReal),
#[error("upload truncated")] UploadTruncated,
#[error("upload corrupted")] UploadCorrupted,
#[error("too many bundles")] TooManyBundles,
#[error("bad bundle: {0}")] BadBundle(String),
#[error("bundle not found")] BundleNotFound,
#[error("bundle(s) in use, cannot clear ({0})")] BundlesInUse(String),
#[error("game spec not found")] GameSpecNotFound,
#[error("game contains invalid UTF-8")] GameSpecInvalidData,
#[error("idle timeout waiting for mgmt command")] IdleTimeout,
#[error("upload took too long (timed out)")] UploadTimeout,
#[error("ssh keys are only for main subaccount")] NoSshKeysForSubaccount,
#[error("ssh key not found")] SshKeyNotFound,
#[error("ssh key id default, ie invalid")] InvalidSshKeyId,
#[error("ssh key invalid: {0}")] InvalidSshKey(#[from] sshkeys::KeyError),
#[error("command forbides account specified")] AccountSpecified,
}
impl AccountDetails {
pub fn default(account: AccountName) -> AccountDetails { AccountDetails {
account,
nick: None, timezone: None, layout: None, access: None,
} }
}
impl From<InternalError> for MgmtError {
fn from(e: InternalError) -> MgmtError {
MgmtError::ServerFailure(format!("internal error: {}", &e))
}
}
impl AccessTokenInfo {
pub fn report(self) -> Vec<String> {
vec![
"Game access url (personal and private):".to_string(),
self.url,
]
}
}