# HG changeset patch # User nemo # Date 1544211308 18000 # Node ID c3ae3cac8fba5ce135dd3bad8ccd5d4ce6ef44b9 # Parent cc99f7c673c7dfa3a37d91d797b1998d293b5582# Parent 7f0166b01dc98c24082259030b5960b27cdf85cf pull 0.9.25 changes into default diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/protocol.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gameServer2/src/protocol.rs Fri Dec 07 14:35:08 2018 -0500 @@ -0,0 +1,47 @@ +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 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/protocol/mod.rs --- a/gameServer2/src/protocol/mod.rs Wed Dec 05 11:34:45 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 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gameServer2/src/server.rs Fri Dec 07 14:35:08 2018 -0500 @@ -0,0 +1,7 @@ +pub mod core; +pub mod client; +pub mod room; +pub mod network; +pub mod coretypes; +mod actions; +mod handlers; diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/actions.rs --- a/gameServer2/src/server/actions.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/actions.rs Fri Dec 07 14:35:08 2018 -0500 @@ -4,7 +4,7 @@ mem::replace }; use super::{ - server::HWServer, + core::HWServer, room::{GameInfo, RoomFlags}, client::HWClient, coretypes::{ClientId, RoomId, GameCfg, VoteType}, diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/core.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gameServer2/src/server/core.rs Fri Dec 07 14:35:08 2018 -0500 @@ -0,0 +1,156 @@ +use slab; +use crate::utils; +use super::{ + client::HWClient, room::HWRoom, actions, handlers, + coretypes::{ClientId, RoomId}, + actions::{Destination, PendingMessage} +}; +use crate::protocol::messages::*; +use rand::{RngCore, thread_rng}; +use base64::{encode}; +use log::*; + +type Slab = slab::Slab; + + +pub struct HWServer { + pub clients: Slab, + pub rooms: Slab, + pub lobby_id: RoomId, + pub output: Vec<(Vec, HWServerMessage)>, + pub removed_clients: Vec, +} + +impl HWServer { + pub fn new(clients_limit: usize, rooms_limit: usize) -> HWServer { + let rooms = Slab::with_capacity(rooms_limit); + let clients = Slab::with_capacity(clients_limit); + let mut server = HWServer { + clients, rooms, + lobby_id: 0, + output: vec![], + removed_clients: vec![] + }; + server.lobby_id = server.add_room(); + server + } + + pub fn add_client(&mut self) -> ClientId { + let key: ClientId; + { + let entry = self.clients.vacant_entry(); + key = entry.key(); + let mut salt = [0u8; 18]; + thread_rng().fill_bytes(&mut salt); + + let client = HWClient::new(entry.key(), encode(&salt)); + entry.insert(client); + } + self.send(key, &Destination::ToSelf, HWServerMessage::Connected(utils::PROTOCOL_VERSION)); + key + } + + pub fn client_lost(&mut self, client_id: ClientId) { + actions::run_action(self, client_id, + actions::Action::ByeClient("Connection reset".to_string())); + } + + pub fn add_room(&mut self) -> RoomId { + let entry = self.rooms.vacant_entry(); + let key = entry.key(); + let room = HWRoom::new(entry.key()); + entry.insert(room); + key + } + + pub fn handle_msg(&mut self, client_id: ClientId, msg: HWProtocolMessage) { + debug!("Handling message {:?} for client {}", msg, client_id); + if self.clients.contains(client_id) { + handlers::handle(self, client_id, msg); + } + } + + fn get_recipients(&self, client_id: ClientId, destination: &Destination) -> Vec { + let mut ids = match *destination { + Destination::ToSelf => vec![client_id], + Destination::ToId(id) => vec![id], + Destination::ToAll {room_id: Some(id), ..} => + self.room_clients(id), + Destination::ToAll {protocol: Some(proto), ..} => + self.protocol_clients(proto), + Destination::ToAll {..} => + self.clients.iter().map(|(id, _)| id).collect::>() + }; + if let Destination::ToAll {skip_self: true, ..} = destination { + if let Some(index) = ids.iter().position(|id| *id == client_id) { + ids.remove(index); + } + } + ids + } + + pub fn send(&mut self, client_id: ClientId, destination: &Destination, message: HWServerMessage) { + let ids = self.get_recipients(client_id, &destination); + self.output.push((ids, message)); + } + + pub fn react(&mut self, client_id: ClientId, actions: Vec) { + for action in actions { + actions::run_action(self, client_id, action); + } + } + + pub fn lobby(&self) -> &HWRoom { &self.rooms[self.lobby_id] } + + pub fn has_room(&self, name: &str) -> bool { + self.rooms.iter().any(|(_, r)| r.name == name) + } + + pub fn find_room(&self, name: &str) -> Option<&HWRoom> { + self.rooms.iter().find_map(|(_, r)| Some(r).filter(|r| r.name == name)) + } + + pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> { + self.rooms.iter_mut().find_map(|(_, r)| Some(r).filter(|r| r.name == name)) + } + + pub fn find_client(&self, nick: &str) -> Option<&HWClient> { + self.clients.iter().find_map(|(_, c)| Some(c).filter(|c| c.nick == nick)) + } + + pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> { + self.clients.iter_mut().find_map(|(_, c)| Some(c).filter(|c| c.nick == nick)) + } + + pub fn select_clients(&self, f: F) -> Vec + where F: Fn(&(usize, &HWClient)) -> bool { + self.clients.iter().filter(f) + .map(|(_, c)| c.id).collect() + } + + pub fn room_clients(&self, room_id: RoomId) -> Vec { + self.select_clients(|(_, c)| c.room_id == Some(room_id)) + } + + pub fn protocol_clients(&self, protocol: u16) -> Vec { + self.select_clients(|(_, c)| c.protocol_number == protocol) + } + + pub fn other_clients_in_room(&self, self_id: ClientId) -> Vec { + let room_id = self.clients[self_id].room_id; + self.select_clients(|(id, c)| *id != self_id && c.room_id == room_id ) + } + + pub fn client_and_room(&mut self, client_id: ClientId) -> (&mut HWClient, Option<&mut HWRoom>) { + let c = &mut self.clients[client_id]; + if let Some(room_id) = c.room_id { + (c, Some(&mut self.rooms[room_id])) + } else { + (c, None) + } + } + + pub fn room(&mut self, client_id: ClientId) -> Option<&mut HWRoom> { + self.client_and_room(client_id).1 + } +} diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gameServer2/src/server/handlers.rs Fri Dec 07 14:35:08 2018 -0500 @@ -0,0 +1,44 @@ +use mio; +use std::{io, io::Write}; + +use super::{ + core::HWServer, + actions::{Action, Action::*}, + coretypes::ClientId +}; +use crate::{ + protocol::messages::{ + HWProtocolMessage, + HWServerMessage::* + } +}; +use log::*; + +mod loggingin; +mod lobby; +mod inroom; +mod common; +mod checker; + +pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) { + match message { + HWProtocolMessage::Ping => + server.react(client_id, vec![Pong.send_self().action()]), + HWProtocolMessage::Quit(Some(msg)) => + server.react(client_id, vec![ByeClient("User quit: ".to_string() + &msg)]), + HWProtocolMessage::Quit(None) => + server.react(client_id, vec![ByeClient("User quit".to_string())]), + HWProtocolMessage::Malformed => warn!("Malformed/unknown message"), + HWProtocolMessage::Empty => warn!("Empty message"), + _ => { + match server.clients[client_id].room_id { + None => + loggingin::handle(server, client_id, message), + Some(id) if id == server.lobby_id => + lobby::handle(server, client_id, message), + Some(id) => + inroom::handle(server, client_id, id, message) + } + }, + } +} diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers/checker.rs --- a/gameServer2/src/server/handlers/checker.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/handlers/checker.rs Fri Dec 07 14:35:08 2018 -0500 @@ -3,7 +3,7 @@ use crate::{ server::{ - server::HWServer, + core::HWServer, coretypes::ClientId, }, protocol::messages::{ diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers/common.rs --- a/gameServer2/src/server/handlers/common.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/handlers/common.rs Fri Dec 07 14:35:08 2018 -0500 @@ -1,5 +1,5 @@ use crate::{ - server::{actions::Action, server::HWServer}, + server::{actions::Action, core::HWServer}, protocol::messages::{ HWProtocolMessage::{self, Rnd}, HWServerMessage::{self, ChatMsg}, } diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers/inroom.rs --- a/gameServer2/src/server/handlers/inroom.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/handlers/inroom.rs Fri Dec 07 14:35:08 2018 -0500 @@ -6,7 +6,7 @@ ClientId, RoomId, Voting, VoteType, GameCfg, MAX_HEDGEHOGS_PER_TEAM }, - server::HWServer, + core::HWServer, room::{HWRoom, RoomFlags}, actions::{Action, Action::*} }, diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers/lobby.rs --- a/gameServer2/src/server/handlers/lobby.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/handlers/lobby.rs Fri Dec 07 14:35:08 2018 -0500 @@ -2,7 +2,7 @@ use crate::{ server::{ - server::HWServer, + core::HWServer, coretypes::ClientId, actions::{Action, Action::*} }, diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers/loggingin.rs --- a/gameServer2/src/server/handlers/loggingin.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/handlers/loggingin.rs Fri Dec 07 14:35:08 2018 -0500 @@ -3,7 +3,7 @@ use crate::{ server::{ client::HWClient, - server::HWServer, + core::HWServer, coretypes::ClientId, actions::{Action, Action::*} }, diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/handlers/mod.rs --- a/gameServer2/src/server/handlers/mod.rs Wed Dec 05 11:34:45 2018 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -use mio; -use std::{io, io::Write}; - -use super::{ - server::HWServer, - actions::{Action, Action::*}, - coretypes::ClientId -}; -use crate::{ - protocol::messages::{ - HWProtocolMessage, - HWServerMessage::* - } -}; -use log::*; - -mod loggingin; -mod lobby; -mod inroom; -mod common; -mod checker; - -pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) { - match message { - HWProtocolMessage::Ping => - server.react(client_id, vec![Pong.send_self().action()]), - HWProtocolMessage::Quit(Some(msg)) => - server.react(client_id, vec![ByeClient("User quit: ".to_string() + &msg)]), - HWProtocolMessage::Quit(None) => - server.react(client_id, vec![ByeClient("User quit".to_string())]), - HWProtocolMessage::Malformed => warn!("Malformed/unknown message"), - HWProtocolMessage::Empty => warn!("Empty message"), - _ => { - match server.clients[client_id].room_id { - None => - loggingin::handle(server, client_id, message), - Some(id) if id == server.lobby_id => - lobby::handle(server, client_id, message), - Some(id) => - inroom::handle(server, client_id, id, message) - } - }, - } -} diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/mod.rs --- a/gameServer2/src/server/mod.rs Wed Dec 05 11:34:45 2018 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -pub mod server; -pub mod client; -pub mod room; -pub mod network; -pub mod coretypes; -mod actions; -mod handlers; diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/network.rs --- a/gameServer2/src/server/network.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/gameServer2/src/server/network.rs Fri Dec 07 14:35:08 2018 -0500 @@ -20,7 +20,7 @@ protocol::{ProtocolDecoder, messages::*} }; use super::{ - server::{HWServer}, + core::{HWServer}, coretypes::ClientId }; #[cfg(feature = "tls-connections")] diff -r 7f0166b01dc9 -r c3ae3cac8fba gameServer2/src/server/server.rs --- a/gameServer2/src/server/server.rs Wed Dec 05 11:34:45 2018 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -use slab; -use crate::utils; -use super::{ - client::HWClient, room::HWRoom, actions, handlers, - coretypes::{ClientId, RoomId}, - actions::{Destination, PendingMessage} -}; -use crate::protocol::messages::*; -use rand::{RngCore, thread_rng}; -use base64::{encode}; -use log::*; - -type Slab = slab::Slab; - - -pub struct HWServer { - pub clients: Slab, - pub rooms: Slab, - pub lobby_id: RoomId, - pub output: Vec<(Vec, HWServerMessage)>, - pub removed_clients: Vec, -} - -impl HWServer { - pub fn new(clients_limit: usize, rooms_limit: usize) -> HWServer { - let rooms = Slab::with_capacity(rooms_limit); - let clients = Slab::with_capacity(clients_limit); - let mut server = HWServer { - clients, rooms, - lobby_id: 0, - output: vec![], - removed_clients: vec![] - }; - server.lobby_id = server.add_room(); - server - } - - pub fn add_client(&mut self) -> ClientId { - let key: ClientId; - { - let entry = self.clients.vacant_entry(); - key = entry.key(); - let mut salt = [0u8; 18]; - thread_rng().fill_bytes(&mut salt); - - let client = HWClient::new(entry.key(), encode(&salt)); - entry.insert(client); - } - self.send(key, &Destination::ToSelf, HWServerMessage::Connected(utils::PROTOCOL_VERSION)); - key - } - - pub fn client_lost(&mut self, client_id: ClientId) { - actions::run_action(self, client_id, - actions::Action::ByeClient("Connection reset".to_string())); - } - - pub fn add_room(&mut self) -> RoomId { - let entry = self.rooms.vacant_entry(); - let key = entry.key(); - let room = HWRoom::new(entry.key()); - entry.insert(room); - key - } - - pub fn handle_msg(&mut self, client_id: ClientId, msg: HWProtocolMessage) { - debug!("Handling message {:?} for client {}", msg, client_id); - if self.clients.contains(client_id) { - handlers::handle(self, client_id, msg); - } - } - - fn get_recipients(&self, client_id: ClientId, destination: &Destination) -> Vec { - let mut ids = match *destination { - Destination::ToSelf => vec![client_id], - Destination::ToId(id) => vec![id], - Destination::ToAll {room_id: Some(id), ..} => - self.room_clients(id), - Destination::ToAll {protocol: Some(proto), ..} => - self.protocol_clients(proto), - Destination::ToAll {..} => - self.clients.iter().map(|(id, _)| id).collect::>() - }; - if let Destination::ToAll {skip_self: true, ..} = destination { - if let Some(index) = ids.iter().position(|id| *id == client_id) { - ids.remove(index); - } - } - ids - } - - pub fn send(&mut self, client_id: ClientId, destination: &Destination, message: HWServerMessage) { - let ids = self.get_recipients(client_id, &destination); - self.output.push((ids, message)); - } - - pub fn react(&mut self, client_id: ClientId, actions: Vec) { - for action in actions { - actions::run_action(self, client_id, action); - } - } - - pub fn lobby(&self) -> &HWRoom { &self.rooms[self.lobby_id] } - - pub fn has_room(&self, name: &str) -> bool { - self.rooms.iter().any(|(_, r)| r.name == name) - } - - pub fn find_room(&self, name: &str) -> Option<&HWRoom> { - self.rooms.iter().find_map(|(_, r)| Some(r).filter(|r| r.name == name)) - } - - pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> { - self.rooms.iter_mut().find_map(|(_, r)| Some(r).filter(|r| r.name == name)) - } - - pub fn find_client(&self, nick: &str) -> Option<&HWClient> { - self.clients.iter().find_map(|(_, c)| Some(c).filter(|c| c.nick == nick)) - } - - pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> { - self.clients.iter_mut().find_map(|(_, c)| Some(c).filter(|c| c.nick == nick)) - } - - pub fn select_clients(&self, f: F) -> Vec - where F: Fn(&(usize, &HWClient)) -> bool { - self.clients.iter().filter(f) - .map(|(_, c)| c.id).collect() - } - - pub fn room_clients(&self, room_id: RoomId) -> Vec { - self.select_clients(|(_, c)| c.room_id == Some(room_id)) - } - - pub fn protocol_clients(&self, protocol: u16) -> Vec { - self.select_clients(|(_, c)| c.protocol_number == protocol) - } - - pub fn other_clients_in_room(&self, self_id: ClientId) -> Vec { - let room_id = self.clients[self_id].room_id; - self.select_clients(|(id, c)| *id != self_id && c.room_id == room_id ) - } - - pub fn client_and_room(&mut self, client_id: ClientId) -> (&mut HWClient, Option<&mut HWRoom>) { - let c = &mut self.clients[client_id]; - if let Some(room_id) = c.room_id { - (c, Some(&mut self.rooms[room_id])) - } else { - (c, None) - } - } - - pub fn room(&mut self, client_id: ClientId) -> Option<&mut HWRoom> { - self.client_and_room(client_id).1 - } -} diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/CMakeLists.txt --- a/qmlfrontend/CMakeLists.txt Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/CMakeLists.txt Fri Dec 07 14:35:08 2018 -0500 @@ -15,6 +15,8 @@ "team.cpp" "team.h" "engine_instance.cpp" "engine_instance.h" "preview_image_provider.cpp" "preview_image_provider.h" - "engine_interface.h") + "engine_interface.h" + "preview_acceptor.cpp" "preview_acceptor.h" + ) target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick) diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/Page1.qml --- a/qmlfrontend/Page1.qml Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/Page1.qml Fri Dec 07 14:35:08 2018 -0500 @@ -2,24 +2,31 @@ import Hedgewars.Engine 1.0 Page1Form { + property var hwEngine + + Component { + id: hwEngineComponent + + HWEngine { + engineLibrary: "./libhedgewars_engine.so" + previewAcceptor: PreviewAcceptor + onPreviewImageChanged: previewImage.source = "image://preview/image" + onPreviewIsRendering: previewImage.source = "qrc:/res/iconTime.png" + } + } + + Component.onCompleted: { + hwEngine = hwEngineComponent.createObject() + } + tickButton.onClicked: { gameView.tick(100) } gameButton.onClicked: { - var engineInstance = HWEngine.runQuickGame() + var engineInstance = hwEngine.runQuickGame() gameView.engineInstance = engineInstance } button1.onClicked: { - HWEngine.getPreview() - } - - Connections { - target: HWEngine - onPreviewImageChanged: { - previewImage.source = "image://preview/image" - } - onPreviewIsRendering: { - previewImage.source = "qrc:/res/iconTime.png" - } + hwEngine.getPreview() } } diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/engine_instance.cpp --- a/qmlfrontend/engine_instance.cpp Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/engine_instance.cpp Fri Dec 07 14:35:08 2018 -0500 @@ -1,6 +1,7 @@ #include "engine_instance.h" #include +#include #include #include @@ -12,37 +13,92 @@ return currentOpenglContext->getProcAddress(fn); } -EngineInstance::EngineInstance(QObject* parent) - : QObject(parent), m_instance(Engine::start_engine()) {} +EngineInstance::EngineInstance(const QString& libraryPath, QObject* parent) + : QObject(parent) { + QLibrary hwlib(libraryPath); + + if (!hwlib.load()) + qWarning() << "Engine library not found" << hwlib.errorString(); + + hedgewars_engine_protocol_version = + reinterpret_cast( + hwlib.resolve("hedgewars_engine_protocol_version")); + start_engine = + reinterpret_cast(hwlib.resolve("start_engine")); + generate_preview = reinterpret_cast( + hwlib.resolve("generate_preview")); + dispose_preview = reinterpret_cast( + hwlib.resolve("dispose_preview")); + cleanup = reinterpret_cast(hwlib.resolve("cleanup")); + + send_ipc = reinterpret_cast(hwlib.resolve("send_ipc")); + read_ipc = reinterpret_cast(hwlib.resolve("read_ipc")); -EngineInstance::~EngineInstance() { Engine::cleanup(m_instance); } + setup_current_gl_context = + reinterpret_cast( + hwlib.resolve("setup_current_gl_context")); + render_frame = + reinterpret_cast(hwlib.resolve("render_frame")); + advance_simulation = reinterpret_cast( + hwlib.resolve("advance_simulation")); + + m_isValid = hedgewars_engine_protocol_version && start_engine && + generate_preview && dispose_preview && cleanup && send_ipc && + read_ipc && setup_current_gl_context && render_frame && + advance_simulation; + emit isValidChanged(m_isValid); + + if (isValid()) { + qDebug() << "Loaded engine library with protocol version" + << hedgewars_engine_protocol_version(); + + m_instance = start_engine(); + } +} + +EngineInstance::~EngineInstance() { + if (m_isValid) cleanup(m_instance); +} void EngineInstance::sendConfig(const GameConfig& config) { for (auto b : config.config()) { - Engine::send_ipc(m_instance, reinterpret_cast(b.data()), - static_cast(b.size())); + send_ipc(m_instance, reinterpret_cast(b.data()), + static_cast(b.size())); } } void EngineInstance::advance(quint32 ticks) { - Engine::advance_simulation(m_instance, ticks); + advance_simulation(m_instance, ticks); } -void EngineInstance::renderFrame() { Engine::render_frame(m_instance); } +void EngineInstance::renderFrame() { render_frame(m_instance); } void EngineInstance::setOpenGLContext(QOpenGLContext* context) { currentOpenglContext = context; auto size = context->surface()->size(); - Engine::setup_current_gl_context( - m_instance, static_cast(size.width()), - static_cast(size.height()), &getProcAddress); + setup_current_gl_context(m_instance, static_cast(size.width()), + static_cast(size.height()), + &getProcAddress); } -Engine::PreviewInfo EngineInstance::generatePreview() { +QImage EngineInstance::generatePreview() { Engine::PreviewInfo pinfo; - Engine::generate_preview(m_instance, &pinfo); + generate_preview(m_instance, &pinfo); + + QVector colorTable; + colorTable.resize(256); + for (int i = 0; i < 256; ++i) colorTable[i] = qRgba(255, 255, 0, i); - return pinfo; + QImage previewImage(pinfo.land, static_cast(pinfo.width), + static_cast(pinfo.height), QImage::Format_Indexed8); + previewImage.setColorTable(colorTable); + + // Cannot use it here, since QImage refers to original bytes + // dispose_preview(m_instance); + + return previewImage; } + +bool EngineInstance::isValid() const { return m_isValid; } diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/engine_instance.h --- a/qmlfrontend/engine_instance.h Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/engine_instance.h Fri Dec 07 14:35:08 2018 -0500 @@ -1,31 +1,50 @@ #ifndef ENGINEINSTANCE_H #define ENGINEINSTANCE_H -#include "engine_interface.h" - +#include #include #include +#include "engine_interface.h" #include "game_config.h" class EngineInstance : public QObject { Q_OBJECT public: - explicit EngineInstance(QObject* parent = nullptr); + explicit EngineInstance(const QString& libraryPath, + QObject* parent = nullptr); ~EngineInstance(); + Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged) + void sendConfig(const GameConfig& config); void advance(quint32 ticks); void renderFrame(); void setOpenGLContext(QOpenGLContext* context); - Engine::PreviewInfo generatePreview(); + QImage generatePreview(); + + bool isValid() const; signals: + void isValidChanged(bool isValid); public slots: private: Engine::EngineInstance* m_instance; + + Engine::hedgewars_engine_protocol_version_t* + hedgewars_engine_protocol_version; + Engine::start_engine_t* start_engine; + Engine::generate_preview_t* generate_preview; + Engine::dispose_preview_t* dispose_preview; + Engine::cleanup_t* cleanup; + Engine::send_ipc_t* send_ipc; + Engine::read_ipc_t* read_ipc; + Engine::setup_current_gl_context_t* setup_current_gl_context; + Engine::render_frame_t* render_frame; + Engine::advance_simulation_t* advance_simulation; + bool m_isValid; }; #endif // ENGINEINSTANCE_H diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/engine_interface.h --- a/qmlfrontend/engine_interface.h Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/engine_interface.h Fri Dec 07 14:35:08 2018 -0500 @@ -18,10 +18,11 @@ unsigned char* land; } PreviewInfo; -typedef uint32_t protocol_version_t(); +typedef uint32_t hedgewars_engine_protocol_version_t(); typedef EngineInstance* start_engine_t(); typedef void generate_preview_t(EngineInstance* engine_state, PreviewInfo* preview); +typedef void dispose_preview_t(EngineInstance* engine_state); typedef void cleanup_t(EngineInstance* engine_state); typedef void send_ipc_t(EngineInstance* engine_state, uint8_t* buf, @@ -36,18 +37,6 @@ typedef bool advance_simulation_t(EngineInstance* engine_state, uint32_t ticks); -extern protocol_version_t* protocol_version; -extern start_engine_t* start_engine; -extern generate_preview_t* generate_preview; -extern cleanup_t* cleanup; - -extern send_ipc_t* send_ipc; -extern read_ipc_t* read_ipc; - -extern setup_current_gl_context_t* setup_current_gl_context; -extern render_frame_t* render_frame; -extern advance_simulation_t* advance_simulation; - #ifdef __cplusplus } }; diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/hwengine.cpp --- a/qmlfrontend/hwengine.cpp Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/hwengine.cpp Fri Dec 07 14:35:08 2018 -0500 @@ -1,64 +1,33 @@ +#include "hwengine.h" + #include -#include -#include +#include #include #include "engine_instance.h" #include "engine_interface.h" #include "game_view.h" -#include "preview_image_provider.h" - -#include "hwengine.h" +#include "preview_acceptor.h" -HWEngine::HWEngine(QQmlEngine* engine, QObject* parent) - : QObject(parent), - m_engine(engine), - m_previewProvider(new PreviewImageProvider()) { - m_engine->addImageProvider(QLatin1String("preview"), m_previewProvider); -} +HWEngine::HWEngine(QObject* parent) : QObject(parent) {} HWEngine::~HWEngine() {} -static QObject* hwengine_singletontype_provider(QQmlEngine* engine, - QJSEngine* scriptEngine) { - Q_UNUSED(scriptEngine) - - HWEngine* hwengine = new HWEngine(engine); - return hwengine; -} - -void HWEngine::exposeToQML() { - qDebug("HWEngine::exposeToQML"); - qmlRegisterSingletonType("Hedgewars.Engine", 1, 0, "HWEngine", - hwengine_singletontype_provider); - qmlRegisterType("Hedgewars.Engine", 1, 0, "GameView"); - qmlRegisterUncreatableType("Hedgewars.Engine", 1, 0, - "EngineInstance", - "Create by HWEngine run methods"); -} - void HWEngine::getPreview() { emit previewIsRendering(); m_gameConfig = GameConfig(); m_gameConfig.cmdSeed(QUuid::createUuid().toByteArray()); - EngineInstance engine; + EngineInstance engine(m_engineLibrary); + if (!engine.isValid()) // TODO: error notification + return; + engine.sendConfig(m_gameConfig); - Engine::PreviewInfo preview = engine.generatePreview(); - - QVector colorTable; - colorTable.resize(256); - for (int i = 0; i < 256; ++i) colorTable[i] = qRgba(255, 255, 0, i); + QImage previewImage = engine.generatePreview(); - QImage previewImage(preview.land, static_cast(preview.width), - static_cast(preview.height), - QImage::Format_Indexed8); - previewImage.setColorTable(colorTable); - previewImage.detach(); - - m_previewProvider->setImage(previewImage); + if (m_previewAcceptor) m_previewAcceptor->setImage(previewImage); emit previewImageChanged(); // m_runQueue->queue(m_gameConfig); @@ -74,9 +43,28 @@ m_gameConfig.cmdTeam(team1); m_gameConfig.cmdTeam(team2); - EngineInstance* engine = new EngineInstance(this); + EngineInstance* engine = new EngineInstance(m_engineLibrary, this); + return engine; // m_runQueue->queue(m_gameConfig); } int HWEngine::previewHedgehogsCount() const { return m_previewHedgehogsCount; } + +PreviewAcceptor* HWEngine::previewAcceptor() const { return m_previewAcceptor; } + +QString HWEngine::engineLibrary() const { return m_engineLibrary; } + +void HWEngine::setPreviewAcceptor(PreviewAcceptor* previewAcceptor) { + if (m_previewAcceptor == previewAcceptor) return; + + m_previewAcceptor = previewAcceptor; + emit previewAcceptorChanged(m_previewAcceptor); +} + +void HWEngine::setEngineLibrary(const QString& engineLibrary) { + if (m_engineLibrary == engineLibrary) return; + + m_engineLibrary = engineLibrary; + emit engineLibraryChanged(m_engineLibrary); +} diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/hwengine.h --- a/qmlfrontend/hwengine.h Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/hwengine.h Fri Dec 07 14:35:08 2018 -0500 @@ -8,25 +8,33 @@ #include "game_config.h" class QQmlEngine; -class PreviewImageProvider; class EngineInstance; +class PreviewAcceptor; class HWEngine : public QObject { Q_OBJECT Q_PROPERTY(int previewHedgehogsCount READ previewHedgehogsCount NOTIFY previewHedgehogsCountChanged) + Q_PROPERTY(PreviewAcceptor* previewAcceptor READ previewAcceptor WRITE + setPreviewAcceptor NOTIFY previewAcceptorChanged) + Q_PROPERTY(QString engineLibrary READ engineLibrary WRITE setEngineLibrary + NOTIFY engineLibraryChanged) public: - explicit HWEngine(QQmlEngine* engine, QObject* parent = nullptr); + explicit HWEngine(QObject* parent = nullptr); ~HWEngine(); - static void exposeToQML(); - Q_INVOKABLE void getPreview(); Q_INVOKABLE EngineInstance* runQuickGame(); int previewHedgehogsCount() const; + PreviewAcceptor* previewAcceptor() const; + QString engineLibrary() const; + + public slots: + void setPreviewAcceptor(PreviewAcceptor* previewAcceptor); + void setEngineLibrary(const QString& engineLibrary); signals: void previewIsRendering(); @@ -34,12 +42,15 @@ void previewHogCountChanged(int count); void gameFinished(); void previewHedgehogsCountChanged(int previewHedgehogsCount); + void previewAcceptorChanged(PreviewAcceptor* previewAcceptor); + void engineLibraryChanged(const QString& engineLibrary); private: QQmlEngine* m_engine; - PreviewImageProvider* m_previewProvider; GameConfig m_gameConfig; int m_previewHedgehogsCount; + PreviewAcceptor* m_previewAcceptor; + QString m_engineLibrary; }; #endif // HWENGINE_H diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/main.cpp --- a/qmlfrontend/main.cpp Wed Dec 05 11:34:45 2018 -0500 +++ b/qmlfrontend/main.cpp Fri Dec 07 14:35:08 2018 -0500 @@ -4,66 +4,34 @@ #include #include "engine_interface.h" +#include "game_view.h" #include "hwengine.h" +#include "preview_acceptor.h" -namespace Engine { -protocol_version_t* protocol_version; -start_engine_t* start_engine; -generate_preview_t* generate_preview; -cleanup_t* cleanup; -send_ipc_t* send_ipc; -read_ipc_t* read_ipc; -setup_current_gl_context_t* setup_current_gl_context; -render_frame_t* render_frame; -advance_simulation_t* advance_simulation; -}; // namespace Engine - -void loadEngineLibrary() { -#ifdef Q_OS_WIN - QLibrary hwlib("./libhedgewars_engine.dll"); -#else - QLibrary hwlib("./libhedgewars_engine.so"); -#endif - - if (!hwlib.load()) - qWarning() << "Engine library not found" << hwlib.errorString(); +namespace Engine {}; // namespace Engine - Engine::protocol_version = reinterpret_cast( - hwlib.resolve("protocol_version")); - Engine::start_engine = - reinterpret_cast(hwlib.resolve("start_engine")); - Engine::generate_preview = reinterpret_cast( - hwlib.resolve("generate_preview")); - Engine::cleanup = - reinterpret_cast(hwlib.resolve("cleanup")); +static QObject* previewacceptor_singletontype_provider( + QQmlEngine* engine, QJSEngine* scriptEngine) { + Q_UNUSED(scriptEngine) - Engine::send_ipc = - reinterpret_cast(hwlib.resolve("send_ipc")); - Engine::read_ipc = - reinterpret_cast(hwlib.resolve("read_ipc")); - - Engine::setup_current_gl_context = - reinterpret_cast( - hwlib.resolve("setup_current_gl_context")); - Engine::render_frame = - reinterpret_cast(hwlib.resolve("render_frame")); - Engine::advance_simulation = reinterpret_cast( - hwlib.resolve("advance_simulation")); - - if (Engine::protocol_version) - qDebug() << "Loaded engine library with protocol version" - << Engine::protocol_version(); + PreviewAcceptor* acceptor = new PreviewAcceptor(engine); + return acceptor; } int main(int argc, char* argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); - loadEngineLibrary(); - QQmlApplicationEngine engine; - HWEngine::exposeToQML(); + qmlRegisterSingletonType( + "Hedgewars.Engine", 1, 0, "PreviewAcceptor", + previewacceptor_singletontype_provider); + qmlRegisterType("Hedgewars.Engine", 1, 0, "HWEngine"); + qmlRegisterType("Hedgewars.Engine", 1, 0, "GameView"); + qmlRegisterUncreatableType("Hedgewars.Engine", 1, 0, + "EngineInstance", + "Create by HWEngine run methods"); engine.load(QUrl(QLatin1String("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/preview_acceptor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlfrontend/preview_acceptor.cpp Fri Dec 07 14:35:08 2018 -0500 @@ -0,0 +1,15 @@ +#include "preview_acceptor.h" + +#include +#include + +#include "preview_image_provider.h" + +PreviewAcceptor::PreviewAcceptor(QQmlEngine *engine, QObject *parent) + : QObject(parent), m_previewProvider(new PreviewImageProvider()) { + engine->addImageProvider(QLatin1String("preview"), m_previewProvider); +} + +void PreviewAcceptor::setImage(const QImage &preview) { + m_previewProvider->setImage(preview); +} diff -r 7f0166b01dc9 -r c3ae3cac8fba qmlfrontend/preview_acceptor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlfrontend/preview_acceptor.h Fri Dec 07 14:35:08 2018 -0500 @@ -0,0 +1,21 @@ +#ifndef PREVIEW_ACCEPTOR_H +#define PREVIEW_ACCEPTOR_H + +#include + +class QQmlEngine; +class PreviewImageProvider; + +class PreviewAcceptor : public QObject { + Q_OBJECT + public: + explicit PreviewAcceptor(QQmlEngine *engine, QObject *parent = nullptr); + + public slots: + void setImage(const QImage &preview); + + private: + PreviewImageProvider *m_previewProvider; +}; + +#endif // PREVIEW_ACCEPTOR_H diff -r 7f0166b01dc9 -r c3ae3cac8fba rust/lib-hedgewars-engine/src/lib.rs --- a/rust/lib-hedgewars-engine/src/lib.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/rust/lib-hedgewars-engine/src/lib.rs Fri Dec 07 14:35:08 2018 -0500 @@ -25,8 +25,8 @@ } #[no_mangle] -pub extern "C" fn protocol_version() -> u32 { - 56 +pub extern "C" fn hedgewars_engine_protocol_version() -> u32 { + 58 } #[no_mangle] @@ -42,14 +42,19 @@ (*engine_state).world.generate_preview(); - let land_preview = (*engine_state).world.preview(); + if let Some(land_preview) = (*engine_state).world.preview() { + *preview = PreviewInfo { + width: land_preview.width() as u32, + height: land_preview.height() as u32, + hedgehogs_number: 0, + land: land_preview.raw_pixels().as_ptr(), + }; + } +} - *preview = PreviewInfo { - width: land_preview.width() as u32, - height: land_preview.height() as u32, - hedgehogs_number: 0, - land: land_preview.raw_pixels().as_ptr(), - }; +#[no_mangle] +pub extern "C" fn dispose_preview(engine_state: &mut EngineInstance, preview: &mut PreviewInfo) { + (*engine_state).world.dispose_preview(); } #[no_mangle] diff -r 7f0166b01dc9 -r c3ae3cac8fba rust/lib-hedgewars-engine/src/world.rs --- a/rust/lib-hedgewars-engine/src/world.rs Wed Dec 05 11:34:45 2018 -0500 +++ b/rust/lib-hedgewars-engine/src/world.rs Fri Dec 07 14:35:08 2018 -0500 @@ -26,7 +26,7 @@ pub struct World { random_numbers_gen: LaggedFibonacciPRNG, - preview: Land2D, + preview: Option>, game_state: Option, } @@ -34,7 +34,7 @@ pub fn new() -> Self { Self { random_numbers_gen: LaggedFibonacciPRNG::new(&[]), - preview: Land2D::new(Size::new(0, 0), 0), + preview: None, game_state: None, } } @@ -43,7 +43,7 @@ self.random_numbers_gen = LaggedFibonacciPRNG::new(seed); } - pub fn preview(&self) -> &Land2D { + pub fn preview(&self) -> &Option> { &self.preview } @@ -63,7 +63,11 @@ let params = LandGenerationParameters::new(0u8, u8::max_value(), 5, false, false); let landgen = TemplatedLandGenerator::new(template()); - self.preview = landgen.generate_land(¶ms, &mut self.random_numbers_gen); + self.preview = Some(landgen.generate_land(¶ms, &mut self.random_numbers_gen)); + } + + pub fn dispose_preview(&mut self) { + self.preview = None } pub fn init(&mut self, template: OutlineTemplate) {