# HG changeset patch
# User nemo
# Date 1544716267 18000
# Node ID 94f10f69fe76df6de61d9e0a8bc142c29111446b
# Parent b33d1c694b1dd5e0f436758a0e137110bd7a3a70# Parent 1ffa8bfc5c58a8aecfe315076f2075d6f3e4ea14
merge in 0.9.25 fixes
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 CMakeLists.txt
--- a/CMakeLists.txt Thu Dec 13 10:49:30 2018 -0500
+++ b/CMakeLists.txt Thu Dec 13 10:51:07 2018 -0500
@@ -85,7 +85,7 @@
set(CPACK_PACKAGE_VERSION_MAJOR 0)
set(CPACK_PACKAGE_VERSION_MINOR 9)
set(CPACK_PACKAGE_VERSION_PATCH 25)
-set(HEDGEWARS_PROTO_VER 57)
+set(HEDGEWARS_PROTO_VER 58)
set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
include(${CMAKE_MODULE_PATH}/revinfo.cmake)
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 ChangeLog.txt
--- a/ChangeLog.txt Thu Dec 13 10:49:30 2018 -0500
+++ b/ChangeLog.txt Thu Dec 13 10:51:07 2018 -0500
@@ -1,5 +1,21 @@
+ features
* bugfixes
+=============== 1.0.0 (unreleased) =================
+ + New chat command: “/help room” (shows room chat commands within the game)
+ + Colorize switching arrows, pointing arrow and target cross in clan color
+ + Longer delays between turns so its easier to see damage and messages
+ + Skip ammo menu animation when playing with turn time of 10s or less
+ * King Mode: Fix team sometimes not being killed properly if king drowned
+ * King Mode: Kill resurrected minions if king is not alive
+ * Fix poison damage not working in first round
+ * Hide most HUD elements in cinematic mode
+ * Don't show "F1", "F2", etc. in ammo menu if these aren't the actual slot keys
+
+Lua API:
+ + New call: SetTurnTimePaused(isPaused): Call with true to pause turn time, false to unpause
+ + New call: GetTurnTimePaused(): Returns true if turn time is paused due to Lua
+ + Params explode, poison in the SpawnFake*Crate functions now optional and default to false
+
====================== 0.9.25 ======================
HIGHLIGHTS:
+ Complete overhaul of Continental supplies
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 QTfrontend/ui/page/pagenet.cpp
--- a/QTfrontend/ui/page/pagenet.cpp Thu Dec 13 10:49:30 2018 -0500
+++ b/QTfrontend/ui/page/pagenet.cpp Thu Dec 13 10:51:07 2018 -0500
@@ -47,6 +47,7 @@
BtnNetConnect = new QPushButton(ConnGroupBox);
BtnNetConnect->setFont(*font14);
BtnNetConnect->setText(QPushButton::tr("Connect"));
+ BtnNetConnect->setWhatsThis(tr("Connect to the selected server"));
GBClayout->addWidget(BtnNetConnect, 2, 2);
tvServersList = new QTableView(ConnGroupBox);
@@ -56,11 +57,13 @@
BtnUpdateSList = new QPushButton(ConnGroupBox);
BtnUpdateSList->setFont(*font14);
BtnUpdateSList->setText(QPushButton::tr("Update"));
+ BtnUpdateSList->setWhatsThis(tr("Update the list of servers"));
GBClayout->addWidget(BtnUpdateSList, 2, 0);
BtnSpecifyServer = new QPushButton(ConnGroupBox);
BtnSpecifyServer->setFont(*font14);
- BtnSpecifyServer->setText(QPushButton::tr("Specify"));
+ BtnSpecifyServer->setText(QPushButton::tr("Specify address"));
+ BtnSpecifyServer->setWhatsThis(tr("Specify the address and port number of a known server and connect to it directly"));
GBClayout->addWidget(BtnSpecifyServer, 2, 1);
return pageLayout;
@@ -71,6 +74,7 @@
QHBoxLayout * footerLayout = new QHBoxLayout();
BtnNetSvrStart = formattedButton(QPushButton::tr("Start server"));
+ BtnNetSvrStart->setWhatsThis(tr("Start private server"));
BtnNetSvrStart->setMinimumSize(180, 50);
QString serverPath = bindir->absolutePath() + "/hedgewars-server";
#ifdef Q_OS_WIN
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 QTfrontend/ui/page/pagesingleplayer.cpp
--- a/QTfrontend/ui/page/pagesingleplayer.cpp Thu Dec 13 10:49:30 2018 -0500
+++ b/QTfrontend/ui/page/pagesingleplayer.cpp Thu Dec 13 10:51:07 2018 -0500
@@ -48,7 +48,7 @@
BtnCampaignPage->setVisible(true);
BtnTrainPage = addButton(":/res/Trainings.png", middleLine, 1, true);
- BtnTrainPage->setWhatsThis(tr("Practice your skills in a range of training missions"));
+ BtnTrainPage->setWhatsThis(tr("Singleplayer missions: Learn how to play in the training, practice your skills in challenges or try to complete goals in scenarios."));
return vLayout;
}
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 QTfrontend/ui/widget/about.cpp
--- a/QTfrontend/ui/widget/about.cpp Thu Dec 13 10:49:30 2018 -0500
+++ b/QTfrontend/ui/widget/about.cpp Thu Dec 13 10:51:07 2018 -0500
@@ -102,6 +102,10 @@
#if defined(__GNUC__)
libinfo.append(QString(tr("GCC: %1")).arg(__VERSION__));
+#elif defined(WIN32_VCPKG)
+ libinfo.append(QString(tr("VC++: %1")).arg(_MSC_FULL_VER));
+#elif defined(__VERSION__)
+ libinfo.append(QString(tr("Unknown Compiler: %1")).arg(__VERSION__));
#else
libinfo.append(QString(tr("Unknown Compiler")));
#endif
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 QTfrontend/ui/widget/mapContainer.cpp
--- a/QTfrontend/ui/widget/mapContainer.cpp Thu Dec 13 10:49:30 2018 -0500
+++ b/QTfrontend/ui/widget/mapContainer.cpp Thu Dec 13 10:51:07 2018 -0500
@@ -921,6 +921,7 @@
QString randomNoMapPrev = tr("Click to randomize the theme and seed");
QString mfsComplex = QString(tr("Adjust the complexity of the generated map"));
QString mfsFortsDistance = QString(tr("Adjust the distance between forts"));
+ QString mfsDrawnMap = QString(tr("Scale size of the drawn map"));
switch (type)
{
case MapModel::GeneratedMap:
@@ -938,6 +939,7 @@
case MapModel::HandDrawnMap:
mapPreview->setWhatsThis(tr("Click to edit"));
btnRandomize->setWhatsThis(randomSeed);
+ mapFeatureSize->setWhatsThis(mfsDrawnMap);
break;
case MapModel::FortsMap:
mapPreview->setWhatsThis(randomNoMapPrev);
@@ -990,7 +992,7 @@
mapgen = MAPGEN_DRAWN;
setMapInfo(MapModel::MapInfoDrawn);
btnLoadMap->show();
- mapFeatureSize->hide();
+ //mapFeatureSize->hide();
btnEditMap->show();
break;
case MapModel::MissionMap:
@@ -1075,14 +1077,15 @@
//if (qAbs(m_prevMapFeatureSize-m_mapFeatureSize) > 4)
{
m_prevMapFeatureSize = m_mapFeatureSize;
- updatePreview();
+ if(m_mapInfo.type!= MapModel::HandDrawnMap)
+ updatePreview();
}
}
// unused because I needed the space for the slider
void HWMapContainer::updateThemeButtonSize()
{
- if (m_mapInfo.type != MapModel::StaticMap && m_mapInfo.type != MapModel::HandDrawnMap)
+ if (m_mapInfo.type != MapModel::StaticMap)
{
btnTheme->setIconSize(QSize(30, 30));
btnTheme->setFixedHeight(30);
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer/Actions.hs
--- a/gameServer/Actions.hs Thu Dec 13 10:49:30 2018 -0500
+++ b/gameServer/Actions.hs Thu Dec 13 10:51:07 2018 -0500
@@ -885,3 +885,13 @@
processAction CheckVotes =
checkVotes >>= mapM_ processAction
+
+processAction (ShowRegisteredOnlyState chans) = do
+ si <- gets serverInfo
+ processAction $ AnswerClients chans
+ ["CHAT", nickServer,
+ if isRegisteredUsersOnly si then
+ loc "This server no longer allows unregistered players to join."
+ else
+ loc "This server now allows unregistered players to join."
+ ]
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer/CoreTypes.hs
--- a/gameServer/CoreTypes.hs Thu Dec 13 10:49:30 2018 -0500
+++ b/gameServer/CoreTypes.hs Thu Dec 13 10:51:07 2018 -0500
@@ -103,6 +103,7 @@
| ReactCmd [B.ByteString]
| CheckVotes
| SetRandomSeed
+ | ShowRegisteredOnlyState [ClientChan]
data Event = LobbyChatMessage
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer/HWProtoCore.hs
--- a/gameServer/HWProtoCore.hs Thu Dec 13 10:49:30 2018 -0500
+++ b/gameServer/HWProtoCore.hs Thu Dec 13 10:51:07 2018 -0500
@@ -107,7 +107,7 @@
-- lobby-only commands
h "STATS" _ = handleCmd_lobbyOnly ["STATS"]
- h "RESTART_SERVER" "YES" = handleCmd_lobbyOnly ["RESTART_SERVER"]
+ h "RESTART_SERVER" p = handleCmd_lobbyOnly ["RESTART_SERVER", upperCase p]
-- room and lobby commands
h "QUIT" _ = handleCmd ["QUIT"]
@@ -120,11 +120,11 @@
h "INFO" n | not $ B.null n = handleCmd ["INFO", n]
h "HELP" _ = handleCmd ["HELP"]
h "REGISTERED_ONLY" _ = serverAdminOnly $ do
- cl <- thisClient
+ rnc <- liftM snd ask
+ let chans = map (sendChan . client rnc) $ allClients rnc
return
[ModifyServerInfo(\s -> s{isRegisteredUsersOnly = not $ isRegisteredUsersOnly s})
- -- TODO: Say whether 'registered only' state is on or off
- , AnswerClients [sendChan cl] ["CHAT", nickServer, loc "'Registered only' state toggled."]
+ , ShowRegisteredOnlyState chans
]
h "SUPER_POWER" _ = serverAdminOnly $ do
cl <- thisClient
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer/HWProtoInRoomState.hs
--- a/gameServer/HWProtoInRoomState.hs Thu Dec 13 10:49:30 2018 -0500
+++ b/gameServer/HWProtoInRoomState.hs Thu Dec 13 10:51:07 2018 -0500
@@ -452,7 +452,7 @@
handleCmd_inRoom ["CALLVOTE"] = do
cl <- thisClient
return [AnswerClients [sendChan cl]
- ["CHAT", nickServer, loc "Available callvote commands: kick , map , pause, newseed, hedgehogs"]
+ ["CHAT", nickServer, loc "Available callvote commands: hedgehogs , pause, newseed, map , kick "]
]
handleCmd_inRoom ["CALLVOTE", "KICK"] = do
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer/HWProtoLobbyState.hs
--- a/gameServer/HWProtoLobbyState.hs Thu Dec 13 10:49:30 2018 -0500
+++ b/gameServer/HWProtoLobbyState.hs Thu Dec 13 10:51:07 2018 -0500
@@ -220,12 +220,18 @@
handleCmd_lobby ["CLEAR_ACCOUNTS_CACHE"] = serverAdminOnly $
return [ClearAccountsCache]
+handleCmd_lobby ["RESTART_SERVER", "YES"] = serverAdminOnly $
+ return [RestartServer]
+
handleCmd_lobby ["RESTART_SERVER"] = serverAdminOnly $
- return [RestartServer]
+ return [Warning $ loc "Please confirm server restart with '/restart_server yes'."]
+
+handleCmd_lobby ["RESTART_SERVER", _] = handleCmd_lobby ["RESTART_SERVER"]
+
handleCmd_lobby ["STATS"] = serverAdminOnly $
return [Stats]
handleCmd_lobby (s:_) = return [ProtocolError $ "Incorrect command '" `B.append` s `B.append` "' (state: in lobby)"]
-handleCmd_lobby [] = return [ProtocolError "Empty command (state: in lobby)"]
\ No newline at end of file
+handleCmd_lobby [] = return [ProtocolError "Empty command (state: in lobby)"]
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer2/Cargo.toml
--- a/gameServer2/Cargo.toml Thu Dec 13 10:49:30 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-[package]
-edition = "2018"
-name = "hedgewars-server"
-version = "0.0.1"
-authors = [ "Andrey Korotaev " ]
-
-[features]
-official-server = ["openssl"]
-tls-connections = ["openssl"]
-default = []
-
-[dependencies]
-rand = "0.5"
-mio = "0.6"
-slab = "0.4"
-netbuf = "0.4"
-nom = "4.1"
-env_logger = "0.6"
-log = "0.4"
-base64 = "0.10"
-bitflags = "1.0"
-serde = "1.0"
-serde_yaml = "0.8"
-serde_derive = "1.0"
-openssl = { version = "0.10", optional = true }
-
-[dev-dependencies]
-proptest = "0.8"
\ No newline at end of file
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer2/src/main.rs
--- a/gameServer2/src/main.rs Thu Dec 13 10:49:30 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-#![allow(unused_imports)]
-#![deny(bare_trait_objects)]
-
-//use std::io::*;
-//use rand::Rng;
-//use std::cmp::Ordering;
-use mio::net::*;
-use mio::*;
-use log::*;
-
-mod utils;
-mod server;
-mod protocol;
-
-use crate::server::network::NetworkLayer;
-use std::time::Duration;
-
-fn main() {
- env_logger::init();
-
- info!("Hedgewars game server, protocol {}", utils::PROTOCOL_VERSION);
-
- let address = "0.0.0.0:46631".parse().unwrap();
- let listener = TcpListener::bind(&address).unwrap();
-
- let poll = Poll::new().unwrap();
- let mut hw_network = NetworkLayer::new(listener, 1024, 512);
- hw_network.register_server(&poll).unwrap();
-
- let mut events = Events::with_capacity(1024);
-
- loop {
- let timeout = if hw_network.has_pending_operations() {
- Some(Duration::from_millis(1))
- } else {
- None
- };
- poll.poll(&mut events, timeout).unwrap();
-
- for event in events.iter() {
- if event.readiness() & Ready::readable() == Ready::readable() {
- match event.token() {
- utils::SERVER => hw_network.accept_client(&poll).unwrap(),
- Token(tok) => hw_network.client_readable(&poll, tok).unwrap(),
- }
- }
- if event.readiness() & Ready::writable() == Ready::writable() {
- match event.token() {
- utils::SERVER => unreachable!(),
- Token(tok) => hw_network.client_writable(&poll, tok).unwrap(),
- }
- }
-// if event.kind().is_hup() || event.kind().is_error() {
-// match event.token() {
-// utils::SERVER => unreachable!(),
-// Token(tok) => server.client_error(&poll, tok).unwrap(),
-// }
-// }
- }
- hw_network.on_idle(&poll).unwrap();
- }
-}
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer2/src/protocol/messages.rs
--- a/gameServer2/src/protocol/messages.rs Thu Dec 13 10:49:30 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,316 +0,0 @@
-use crate::server::coretypes::{
- ServerVar, GameCfg, TeamInfo,
- HedgehogInfo, VoteType
-};
-use std::{ops, convert::From, iter::once};
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum HWProtocolMessage {
- // core
- Ping,
- Pong,
- Quit(Option),
- //Cmd(String, Vec),
- Global(String),
- Watch(String),
- ToggleServerRegisteredOnly,
- SuperPower,
- Info(String),
- // not entered state
- Nick(String),
- Proto(u16),
- Password(String, String),
- Checker(u16, String, String),
- // lobby
- List,
- Chat(String),
- CreateRoom(String, Option),
- JoinRoom(String, Option),
- Follow(String),
- Rnd(Vec),
- Kick(String),
- Ban(String, String, u32),
- BanIP(String, String, u32),
- BanNick(String, String, u32),
- BanList,
- Unban(String),
- SetServerVar(ServerVar),
- GetServerVar,
- RestartServer,
- Stats,
- // in room
- Part(Option),
- Cfg(GameCfg),
- AddTeam(Box),
- RemoveTeam(String),
- SetHedgehogsNumber(String, u8),
- SetTeamColor(String, u8),
- ToggleReady,
- StartGame,
- EngineMessage(String),
- RoundFinished,
- ToggleRestrictJoin,
- ToggleRestrictTeams,
- ToggleRegisteredOnly,
- RoomName(String),
- Delegate(String),
- TeamChat(String),
- MaxTeams(u8),
- Fix,
- Unfix,
- Greeting(String),
- CallVote(Option),
- Vote(bool),
- ForceVote(bool),
- Save(String, String),
- Delete(String),
- SaveRoom(String),
- LoadRoom(String),
- Malformed,
- Empty,
-}
-
-#[derive(Debug)]
-pub enum HWServerMessage {
- Ping,
- Pong,
- Bye(String),
- Nick(String),
- Proto(u16),
- ServerAuth(String),
- LobbyLeft(String, String),
- LobbyJoined(Vec),
- ChatMsg {nick: String, msg: String},
- ClientFlags(String, Vec),
- Rooms(Vec),
- RoomAdd(Vec),
- RoomJoined(Vec),
- RoomLeft(String, String),
- RoomRemove(String),
- RoomUpdated(String, Vec),
- TeamAdd(Vec),
- TeamRemove(String),
- TeamAccepted(String),
- TeamColor(String, u8),
- HedgehogsNumber(String, u8),
- ConfigEntry(String, Vec),
- Kicked,
- RunGame,
- ForwardEngineMessage(Vec),
- RoundFinished,
-
- ServerMessage(String),
- Notice(String),
- Warning(String),
- Error(String),
- Connected(u32),
- Unreachable,
-
- //Deprecated messages
- LegacyReady(bool, Vec)
-}
-
-pub fn server_chat(msg: String) -> HWServerMessage {
- HWServerMessage::ChatMsg{ nick: "[server]".to_string(), msg }
-}
-
-impl GameCfg {
- pub fn to_protocol(&self) -> (String, Vec) {
- use crate::server::coretypes::GameCfg::*;
- match self {
- FeatureSize(s) => ("FEATURE_SIZE".to_string(), vec![s.to_string()]),
- MapType(t) => ("MAP".to_string(), vec![t.to_string()]),
- MapGenerator(g) => ("MAPGEN".to_string(), vec![g.to_string()]),
- MazeSize(s) => ("MAZE_SIZE".to_string(), vec![s.to_string()]),
- Seed(s) => ("SEED".to_string(), vec![s.to_string()]),
- Template(t) => ("TEMPLATE".to_string(), vec![t.to_string()]),
-
- Ammo(n, None) => ("AMMO".to_string(), vec![n.to_string()]),
- Ammo(n, Some(s)) => ("AMMO".to_string(), vec![n.to_string(), s.to_string()]),
- Scheme(n, s) if s.is_empty() => ("SCHEME".to_string(), vec![n.to_string()]),
- Scheme(n, s) => ("SCHEME".to_string(), {
- let mut v = vec![n.to_string()];
- v.extend(s.clone().into_iter());
- v
- }),
- Script(s) => ("SCRIPT".to_string(), vec![s.to_string()]),
- Theme(t) => ("THEME".to_string(), vec![t.to_string()]),
- DrawnMap(m) => ("DRAWNMAP".to_string(), vec![m.to_string()])
- }
- }
-
- pub fn to_server_msg(&self) -> HWServerMessage {
- use self::HWServerMessage::ConfigEntry;
- let (name, args) = self.to_protocol();
- HWServerMessage::ConfigEntry(name, args)
- }
-}
-
-macro_rules! const_braces {
- ($e: expr) => { "{}\n" }
-}
-
-macro_rules! msg {
- [$($part: expr),*] => {
- format!(concat!($(const_braces!($part)),*, "\n"), $($part),*);
- };
-}
-
-#[cfg(test)]
-macro_rules! several {
- [$part: expr] => { once($part) };
- [$part: expr, $($other: expr),*] => { once($part).chain(several![$($other),*]) };
-}
-
-impl HWProtocolMessage {
- /** Converts the message to a raw `String`, which can be sent over the network.
- *
- * This is the inverse of the `message` parser.
- */
- #[cfg(test)]
- pub(crate) fn to_raw_protocol(&self) -> String {
- use self::HWProtocolMessage::*;
- match self {
- Ping => msg!["PING"],
- Pong => msg!["PONG"],
- Quit(None) => msg!["QUIT"],
- Quit(Some(msg)) => msg!["QUIT", msg],
- Global(msg) => msg!["CMD", format!("GLOBAL {}", msg)],
- Watch(name) => msg!["CMD", format!("WATCH {}", name)],
- ToggleServerRegisteredOnly => msg!["CMD", "REGISTERED_ONLY"],
- SuperPower => msg!["CMD", "SUPER_POWER"],
- Info(info) => msg!["CMD", format!("INFO {}", info)],
- Nick(nick) => msg!("NICK", nick),
- Proto(version) => msg!["PROTO", version],
- Password(p, s) => msg!["PASSWORD", p, s],
- Checker(i, n, p) => msg!["CHECKER", i, n, p],
- List => msg!["LIST"],
- Chat(msg) => msg!["CHAT", msg],
- CreateRoom(name, None) => msg!["CREATE_ROOM", name],
- CreateRoom(name, Some(password)) =>
- msg!["CREATE_ROOM", name, password],
- JoinRoom(name, None) => msg!["JOIN_ROOM", name],
- JoinRoom(name, Some(password)) =>
- msg!["JOIN_ROOM", name, password],
- Follow(name) => msg!["FOLLOW", name],
- Rnd(args) => if args.is_empty() {
- msg!["CMD", "RND"]
- } else {
- msg!["CMD", format!("RND {}", args.join(" "))]
- },
- Kick(name) => msg!["KICK", name],
- Ban(name, reason, time) => msg!["BAN", name, reason, time],
- BanIP(ip, reason, time) => msg!["BAN_IP", ip, reason, time],
- BanNick(nick, reason, time) =>
- msg!("BAN_NICK", nick, reason, time),
- BanList => msg!["BANLIST"],
- Unban(name) => msg!["UNBAN", name],
- //SetServerVar(ServerVar), ???
- GetServerVar => msg!["GET_SERVER_VAR"],
- RestartServer => msg!["CMD", "RESTART_SERVER YES"],
- Stats => msg!["CMD", "STATS"],
- Part(None) => msg!["PART"],
- Part(Some(msg)) => msg!["PART", msg],
- Cfg(config) => {
- let (name, args) = config.to_protocol();
- msg!["CFG", name, args.join("\n")]
- },
- AddTeam(info) =>
- msg!["ADD_TEAM", info.name, info.color, info.grave, info.fort,
- info.voice_pack, info.flag, info.difficulty,
- info.hedgehogs.iter()
- .flat_map(|h| several![&h.name[..], &h.hat[..]])
- .collect::>().join("\n")],
- RemoveTeam(name) => msg!["REMOVE_TEAM", name],
- SetHedgehogsNumber(team, number) => msg!["HH_NUM", team, number],
- SetTeamColor(team, color) => msg!["TEAM_COLOR", team, color],
- ToggleReady => msg!["TOGGLE_READY"],
- StartGame => msg!["START_GAME"],
- EngineMessage(msg) => msg!["EM", msg],
- RoundFinished => msg!["ROUNDFINISHED"],
- ToggleRestrictJoin => msg!["TOGGLE_RESTRICT_JOINS"],
- ToggleRestrictTeams => msg!["TOGGLE_RESTRICT_TEAMS"],
- ToggleRegisteredOnly => msg!["TOGGLE_REGISTERED_ONLY"],
- RoomName(name) => msg!["ROOM_NAME", name],
- Delegate(name) => msg!["CMD", format!("DELEGATE {}", name)],
- TeamChat(msg) => msg!["TEAMCHAT", msg],
- MaxTeams(count) => msg!["CMD", format!("MAXTEAMS {}", count)] ,
- Fix => msg!["CMD", "FIX"],
- Unfix => msg!["CMD", "UNFIX"],
- Greeting(msg) => msg!["CMD", format!("GREETING {}", msg)],
- //CallVote(Option<(String, Option)>) =>, ??
- Vote(msg) => msg!["CMD", format!("VOTE {}", if *msg {"YES"} else {"NO"})],
- ForceVote(msg) => msg!["CMD", format!("FORCE {}", if *msg {"YES"} else {"NO"})],
- Save(name, location) => msg!["CMD", format!("SAVE {} {}", name, location)],
- Delete(name) => msg!["CMD", format!("DELETE {}", name)],
- SaveRoom(name) => msg!["CMD", format!("SAVEROOM {}", name)],
- LoadRoom(name) => msg!["CMD", format!("LOADROOM {}", name)],
- Malformed => msg!["A", "QUICK", "BROWN", "HOG", "JUMPS", "OVER", "THE", "LAZY", "DOG"],
- Empty => msg![""],
- _ => panic!("Protocol message not yet implemented")
- }
- }
-}
-
-fn construct_message(header: &[&str], msg: &[String]) -> String {
- let mut v: Vec<_> = header.iter().cloned().collect();
- v.extend(msg.iter().map(|s| &s[..]));
- v.push("\n");
- v.join("\n")
-}
-
-impl HWServerMessage {
- pub fn to_raw_protocol(&self) -> String {
- use self::HWServerMessage::*;
- match self {
- Ping => msg!["PING"],
- Pong => msg!["PONG"],
- Connected(protocol_version) => msg![
- "CONNECTED",
- "Hedgewars server https://www.hedgewars.org/",
- protocol_version],
- Bye(msg) => msg!["BYE", msg],
- Nick(nick) => msg!["NICK", nick],
- Proto(proto) => msg!["PROTO", proto],
- ServerAuth(hash) => msg!["SERVER_AUTH", hash],
- LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg],
- LobbyJoined(nicks) =>
- construct_message(&["LOBBY:JOINED"], &nicks),
- ClientFlags(flags, nicks) =>
- construct_message(&["CLIENT_FLAGS", flags], &nicks),
- Rooms(info) =>
- construct_message(&["ROOMS"], &info),
- RoomAdd(info) =>
- construct_message(&["ROOM", "ADD"], &info),
- RoomJoined(nicks) =>
- construct_message(&["JOINED"], &nicks),
- RoomLeft(nick, msg) => msg!["LEFT", nick, msg],
- RoomRemove(name) => msg!["ROOM", "DEL", name],
- RoomUpdated(name, info) =>
- construct_message(&["ROOM", "UPD", name], &info),
- TeamAdd(info) =>
- construct_message(&["ADD_TEAM"], &info),
- TeamRemove(name) => msg!["REMOVE_TEAM", name],
- TeamAccepted(name) => msg!["TEAM_ACCEPTED", name],
- TeamColor(name, color) => msg!["TEAM_COLOR", name, color],
- HedgehogsNumber(name, number) => msg!["HH_NUM", name, number],
- ConfigEntry(name, values) =>
- construct_message(&["CFG", name], &values),
- Kicked => msg!["KICKED"],
- RunGame => msg!["RUN_GAME"],
- ForwardEngineMessage(em) =>
- construct_message(&["EM"], &em),
- RoundFinished => msg!["ROUND_FINISHED"],
- ChatMsg {nick, msg} => msg!["CHAT", nick, msg],
- ServerMessage(msg) => msg!["SERVER_MESSAGE", msg],
- Notice(msg) => msg!["NOTICE", msg],
- Warning(msg) => msg!["WARNING", msg],
- Error(msg) => msg!["ERROR", msg],
-
- LegacyReady(is_ready, nicks) =>
- construct_message(&[if *is_ready {"READY"} else {"NOT_READY"}], &nicks),
-
- _ => msg!["ERROR", "UNIMPLEMENTED"],
- }
- }
-}
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer2/src/protocol/mod.rs
--- a/gameServer2/src/protocol/mod.rs Thu Dec 13 10:49:30 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-use netbuf;
-use std::{
- io::{Read, Result}
-};
-use nom::{
- IResult, Err
-};
-
-pub mod messages;
-#[cfg(test)]
-pub mod test;
-mod parser;
-
-pub struct ProtocolDecoder {
- buf: netbuf::Buf,
- consumed: usize,
-}
-
-impl ProtocolDecoder {
- pub fn new() -> ProtocolDecoder {
- ProtocolDecoder {
- buf: netbuf::Buf::new(),
- consumed: 0,
- }
- }
-
- pub fn read_from(&mut self, stream: &mut R) -> Result {
- self.buf.read_from(stream)
- }
-
- pub fn extract_messages(&mut self) -> Vec {
- let parse_result = parser::extract_messages(&self.buf[..]);
- match parse_result {
- Ok((tail, msgs)) => {
- self.consumed = self.buf.len() - self.consumed - tail.len();
- msgs
- },
- Err(Err::Incomplete(_)) => unreachable!(),
- Err(Err::Error(_)) | Err(Err::Failure(_)) => unreachable!(),
- }
- }
-
- pub fn sweep(&mut self) {
- self.buf.consume(self.consumed);
- self.consumed = 0;
- }
-}
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer2/src/protocol/parser.rs
--- a/gameServer2/src/protocol/parser.rs Thu Dec 13 10:49:30 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,284 +0,0 @@
-/** The parsers for the chat and multiplayer protocol. The main parser is `message`.
- * # Protocol
- * All messages consist of `\n`-separated strings. The end of a message is
- * indicated by a double newline - `\n\n`.
- *
- * For example, a nullary command like PING will be actually sent as `PING\n\n`.
- * A unary command, such as `START_GAME nick` will be actually sent as `START_GAME\nnick\n\n`.
- */
-
-use nom::*;
-
-use std::{
- str, str::FromStr,
- ops::Range
-};
-use super::{
- messages::{HWProtocolMessage, HWProtocolMessage::*}
-};
-#[cfg(test)]
-use {
- super::test::gen_proto_msg,
- proptest::{proptest, proptest_helper}
-};
-use crate::server::coretypes::{
- HedgehogInfo, TeamInfo, GameCfg, VoteType, MAX_HEDGEHOGS_PER_TEAM
-};
-
-named!(end_of_message, tag!("\n\n"));
-named!(str_line<&[u8], &str>, map_res!(not_line_ending, str::from_utf8));
-named!( a_line<&[u8], String>, map!(str_line, String::from));
-named!(cmd_arg<&[u8], String>,
- map!(map_res!(take_until_either!(" \n"), str::from_utf8), String::from));
-named!( u8_line<&[u8], u8>, map_res!(str_line, FromStr::from_str));
-named!(u16_line<&[u8], u16>, map_res!(str_line, FromStr::from_str));
-named!(u32_line<&[u8], u32>, map_res!(str_line, FromStr::from_str));
-named!(yes_no_line<&[u8], bool>, alt!(
- do_parse!(tag_no_case!("YES") >> (true))
- | do_parse!(tag_no_case!("NO") >> (false))));
-named!(opt_param<&[u8], Option >, alt!(
- do_parse!(peek!(tag!("\n\n")) >> (None))
- | do_parse!(tag!("\n") >> s: str_line >> (Some(s.to_string())))));
-named!(spaces<&[u8], &[u8]>, preceded!(tag!(" "), eat_separator!(" ")));
-named!(opt_space_param<&[u8], Option >, alt!(
- do_parse!(peek!(tag!("\n\n")) >> (None))
- | do_parse!(spaces >> s: str_line >> (Some(s.to_string())))));
-named!(hog_line<&[u8], HedgehogInfo>,
- do_parse!(name: str_line >> eol >> hat: str_line >>
- (HedgehogInfo{name: name.to_string(), hat: hat.to_string()})));
-named!(_8_hogs<&[u8], [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize]>,
- do_parse!(h1: hog_line >> eol >> h2: hog_line >> eol >>
- h3: hog_line >> eol >> h4: hog_line >> eol >>
- h5: hog_line >> eol >> h6: hog_line >> eol >>
- h7: hog_line >> eol >> h8: hog_line >>
- ([h1, h2, h3, h4, h5, h6, h7, h8])));
-named!(voting<&[u8], VoteType>, alt!(
- do_parse!(tag_no_case!("KICK") >> spaces >> n: a_line >>
- (VoteType::Kick(n)))
- | do_parse!(tag_no_case!("MAP") >>
- n: opt!(preceded!(spaces, a_line)) >>
- (VoteType::Map(n)))
- | do_parse!(tag_no_case!("PAUSE") >>
- (VoteType::Pause))
- | do_parse!(tag_no_case!("NEWSEED") >>
- (VoteType::NewSeed))
- | do_parse!(tag_no_case!("HEDGEHOGS") >> spaces >> n: u8_line >>
- (VoteType::HedgehogsPerTeam(n)))));
-
-/** Recognizes messages which do not take any parameters */
-named!(basic_message<&[u8], HWProtocolMessage>, alt!(
- do_parse!(tag!("PING") >> (Ping))
- | do_parse!(tag!("PONG") >> (Pong))
- | do_parse!(tag!("LIST") >> (List))
- | do_parse!(tag!("BANLIST") >> (BanList))
- | do_parse!(tag!("GET_SERVER_VAR") >> (GetServerVar))
- | do_parse!(tag!("TOGGLE_READY") >> (ToggleReady))
- | do_parse!(tag!("START_GAME") >> (StartGame))
- | do_parse!(tag!("ROUNDFINISHED") >> _m: opt_param >> (RoundFinished))
- | do_parse!(tag!("TOGGLE_RESTRICT_JOINS") >> (ToggleRestrictJoin))
- | do_parse!(tag!("TOGGLE_RESTRICT_TEAMS") >> (ToggleRestrictTeams))
- | do_parse!(tag!("TOGGLE_REGISTERED_ONLY") >> (ToggleRegisteredOnly))
-));
-
-/** Recognizes messages which take exactly one parameter */
-named!(one_param_message<&[u8], HWProtocolMessage>, alt!(
- do_parse!(tag!("NICK") >> eol >> n: a_line >> (Nick(n)))
- | do_parse!(tag!("INFO") >> eol >> n: a_line >> (Info(n)))
- | do_parse!(tag!("CHAT") >> eol >> m: a_line >> (Chat(m)))
- | do_parse!(tag!("PART") >> msg: opt_param >> (Part(msg)))
- | do_parse!(tag!("FOLLOW") >> eol >> n: a_line >> (Follow(n)))
- | do_parse!(tag!("KICK") >> eol >> n: a_line >> (Kick(n)))
- | do_parse!(tag!("UNBAN") >> eol >> n: a_line >> (Unban(n)))
- | do_parse!(tag!("EM") >> eol >> m: a_line >> (EngineMessage(m)))
- | do_parse!(tag!("TEAMCHAT") >> eol >> m: a_line >> (TeamChat(m)))
- | do_parse!(tag!("ROOM_NAME") >> eol >> n: a_line >> (RoomName(n)))
- | do_parse!(tag!("REMOVE_TEAM") >> eol >> n: a_line >> (RemoveTeam(n)))
-
- | do_parse!(tag!("PROTO") >> eol >> d: u16_line >> (Proto(d)))
-
- | do_parse!(tag!("QUIT") >> msg: opt_param >> (Quit(msg)))
-));
-
-/** Recognizes messages preceded with CMD */
-named!(cmd_message<&[u8], HWProtocolMessage>, preceded!(tag!("CMD\n"), alt!(
- do_parse!(tag_no_case!("STATS") >> (Stats))
- | do_parse!(tag_no_case!("FIX") >> (Fix))
- | do_parse!(tag_no_case!("UNFIX") >> (Unfix))
- | do_parse!(tag_no_case!("RESTART_SERVER") >> spaces >> tag!("YES") >> (RestartServer))
- | do_parse!(tag_no_case!("REGISTERED_ONLY") >> (ToggleServerRegisteredOnly))
- | do_parse!(tag_no_case!("SUPER_POWER") >> (SuperPower))
- | do_parse!(tag_no_case!("PART") >> m: opt_space_param >> (Part(m)))
- | do_parse!(tag_no_case!("QUIT") >> m: opt_space_param >> (Quit(m)))
- | do_parse!(tag_no_case!("DELEGATE") >> spaces >> n: a_line >> (Delegate(n)))
- | do_parse!(tag_no_case!("SAVE") >> spaces >> n: cmd_arg >> spaces >> l: cmd_arg >> (Save(n, l)))
- | do_parse!(tag_no_case!("DELETE") >> spaces >> n: a_line >> (Delete(n)))
- | do_parse!(tag_no_case!("SAVEROOM") >> spaces >> r: a_line >> (SaveRoom(r)))
- | do_parse!(tag_no_case!("LOADROOM") >> spaces >> r: a_line >> (LoadRoom(r)))
- | do_parse!(tag_no_case!("GLOBAL") >> spaces >> m: a_line >> (Global(m)))
- | do_parse!(tag_no_case!("WATCH") >> spaces >> i: a_line >> (Watch(i)))
- | do_parse!(tag_no_case!("GREETING") >> spaces >> m: a_line >> (Greeting(m)))
- | do_parse!(tag_no_case!("VOTE") >> spaces >> m: yes_no_line >> (Vote(m)))
- | do_parse!(tag_no_case!("FORCE") >> spaces >> m: yes_no_line >> (ForceVote(m)))
- | do_parse!(tag_no_case!("INFO") >> spaces >> n: a_line >> (Info(n)))
- | do_parse!(tag_no_case!("MAXTEAMS") >> spaces >> n: u8_line >> (MaxTeams(n)))
- | do_parse!(tag_no_case!("CALLVOTE") >>
- v: opt!(preceded!(spaces, voting)) >> (CallVote(v)))
- | do_parse!(
- tag_no_case!("RND") >> alt!(spaces | peek!(end_of_message)) >>
- v: str_line >>
- (Rnd(v.split_whitespace().map(String::from).collect())))
-)));
-
-named!(complex_message<&[u8], HWProtocolMessage>, alt!(
- do_parse!(tag!("PASSWORD") >> eol >>
- p: a_line >> eol >>
- s: a_line >>
- (Password(p, s)))
- | do_parse!(tag!("CHECKER") >> eol >>
- i: u16_line >> eol >>
- n: a_line >> eol >>
- p: a_line >>
- (Checker(i, n, p)))
- | do_parse!(tag!("CREATE_ROOM") >> eol >>
- n: a_line >>
- p: opt_param >>
- (CreateRoom(n, p)))
- | do_parse!(tag!("JOIN_ROOM") >> eol >>
- n: a_line >>
- p: opt_param >>
- (JoinRoom(n, p)))
- | do_parse!(tag!("ADD_TEAM") >> eol >>
- name: a_line >> eol >>
- color: u8_line >> eol >>
- grave: a_line >> eol >>
- fort: a_line >> eol >>
- voice_pack: a_line >> eol >>
- flag: a_line >> eol >>
- difficulty: u8_line >> eol >>
- hedgehogs: _8_hogs >>
- (AddTeam(Box::new(TeamInfo{
- name, color, grave, fort,
- voice_pack, flag, difficulty,
- hedgehogs, hedgehogs_number: 0
- }))))
- | do_parse!(tag!("HH_NUM") >> eol >>
- n: a_line >> eol >>
- c: u8_line >>
- (SetHedgehogsNumber(n, c)))
- | do_parse!(tag!("TEAM_COLOR") >> eol >>
- n: a_line >> eol >>
- c: u8_line >>
- (SetTeamColor(n, c)))
- | do_parse!(tag!("BAN") >> eol >>
- n: a_line >> eol >>
- r: a_line >> eol >>
- t: u32_line >>
- (Ban(n, r, t)))
- | do_parse!(tag!("BAN_IP") >> eol >>
- n: a_line >> eol >>
- r: a_line >> eol >>
- t: u32_line >>
- (BanIP(n, r, t)))
- | do_parse!(tag!("BAN_NICK") >> eol >>
- n: a_line >> eol >>
- r: a_line >> eol >>
- t: u32_line >>
- (BanNick(n, r, t)))
-));
-
-named!(cfg_message<&[u8], HWProtocolMessage>, preceded!(tag!("CFG\n"), map!(alt!(
- do_parse!(tag!("THEME") >> eol >>
- name: a_line >>
- (GameCfg::Theme(name)))
- | do_parse!(tag!("SCRIPT") >> eol >>
- name: a_line >>
- (GameCfg::Script(name)))
- | do_parse!(tag!("AMMO") >> eol >>
- name: a_line >>
- value: opt_param >>
- (GameCfg::Ammo(name, value)))
- | do_parse!(tag!("SCHEME") >> eol >>
- name: a_line >>
- values: opt!(preceded!(eol, separated_list!(eol, a_line))) >>
- (GameCfg::Scheme(name, values.unwrap_or_default())))
- | do_parse!(tag!("FEATURE_SIZE") >> eol >>
- value: u32_line >>
- (GameCfg::FeatureSize(value)))
- | do_parse!(tag!("MAP") >> eol >>
- value: a_line >>
- (GameCfg::MapType(value)))
- | do_parse!(tag!("MAPGEN") >> eol >>
- value: u32_line >>
- (GameCfg::MapGenerator(value)))
- | do_parse!(tag!("MAZE_SIZE") >> eol >>
- value: u32_line >>
- (GameCfg::MazeSize(value)))
- | do_parse!(tag!("SEED") >> eol >>
- value: a_line >>
- (GameCfg::Seed(value)))
- | do_parse!(tag!("TEMPLATE") >> eol >>
- value: u32_line >>
- (GameCfg::Template(value)))
- | do_parse!(tag!("DRAWNMAP") >> eol >>
- value: a_line >>
- (GameCfg::DrawnMap(value)))
-), Cfg)));
-
-named!(malformed_message<&[u8], HWProtocolMessage>,
- do_parse!(separated_list!(eol, a_line) >> (Malformed)));
-
-named!(empty_message<&[u8], HWProtocolMessage>,
- do_parse!(alt!(end_of_message | eol) >> (Empty)));
-
-named!(message<&[u8], HWProtocolMessage>, alt!(terminated!(
- alt!(
- basic_message
- | one_param_message
- | cmd_message
- | complex_message
- | cfg_message
- ), end_of_message
- )
- | terminated!(malformed_message, end_of_message)
- | empty_message
- )
-);
-
-named!(pub extract_messages<&[u8], Vec >, many0!(complete!(message)));
-
-#[cfg(test)]
-proptest! {
- #[test]
- fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) {
- println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes());
- assert_eq!(message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone())))
- }
-}
-
-#[test]
-fn parse_test() {
- assert_eq!(message(b"PING\n\n"), Ok((&b""[..], Ping)));
- assert_eq!(message(b"START_GAME\n\n"), Ok((&b""[..], StartGame)));
- assert_eq!(message(b"NICK\nit's me\n\n"), Ok((&b""[..], Nick("it's me".to_string()))));
- assert_eq!(message(b"PROTO\n51\n\n"), Ok((&b""[..], Proto(51))));
- assert_eq!(message(b"QUIT\nbye-bye\n\n"), Ok((&b""[..], Quit(Some("bye-bye".to_string())))));
- assert_eq!(message(b"QUIT\n\n"), Ok((&b""[..], Quit(None))));
- assert_eq!(message(b"CMD\nwatch demo\n\n"), Ok((&b""[..], Watch("demo".to_string()))));
- assert_eq!(message(b"BAN\nme\nbad\n77\n\n"), Ok((&b""[..], Ban("me".to_string(), "bad".to_string(), 77))));
-
- assert_eq!(message(b"CMD\nPART\n\n"), Ok((&b""[..], Part(None))));
- assert_eq!(message(b"CMD\nPART _msg_\n\n"), Ok((&b""[..], Part(Some("_msg_".to_string())))));
-
- assert_eq!(message(b"CMD\nRND\n\n"), Ok((&b""[..], Rnd(vec![]))));
- assert_eq!(
- message(b"CMD\nRND A B\n\n"),
- Ok((&b""[..], Rnd(vec![String::from("A"), String::from("B")])))
- );
-
- assert_eq!(extract_messages(b"QUIT\n1\n2\n\n"), Ok((&b""[..], vec![Malformed])));
-
- assert_eq!(extract_messages(b"PING\n\nPING\n\nP"), Ok((&b"P"[..], vec![Ping, Ping])));
- assert_eq!(extract_messages(b"SING\n\nPING\n\n"), Ok((&b""[..], vec![Malformed, Ping])));
- assert_eq!(extract_messages(b"\n\n\n\nPING\n\n"), Ok((&b""[..], vec![Empty, Empty, Ping])));
- assert_eq!(extract_messages(b"\n\n\nPING\n\n"), Ok((&b""[..], vec![Empty, Empty, Ping])));
-}
diff -r 1ffa8bfc5c58 -r 94f10f69fe76 gameServer2/src/protocol/test.rs
--- a/gameServer2/src/protocol/test.rs Thu Dec 13 10:49:30 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-use proptest::{
- test_runner::{TestRunner, Reason},
- arbitrary::{any, any_with, Arbitrary, StrategyFor},
- strategy::{Strategy, BoxedStrategy, Just, Map}
-};
-
-use crate::server::coretypes::{GameCfg, TeamInfo, HedgehogInfo};
-
-use super::messages::{
- HWProtocolMessage, HWProtocolMessage::*
-};
-
-// Due to inability to define From between Options
-trait Into2: Sized { fn into2(self) -> T; }
-impl Into2 for T { fn into2(self) -> T { self } }
-impl Into2> for Vec {
- fn into2(self) -> Vec {
- self.into_iter().map(|x| x.0).collect()
- }
-}
-impl Into2 for Ascii { fn into2(self) -> String { self.0 } }
-impl Into2