network fixes and improvements, code refactoring

This commit is contained in:
milek7
2019-02-03 23:24:34 +01:00
parent 05ba7c653b
commit 85ad17af3c
29 changed files with 383 additions and 295 deletions

View File

@@ -74,4 +74,8 @@ enum class TCommandType
using material_handle = int;
using texture_handle = int;
struct invalid_scenery_exception : std::runtime_error {
invalid_scenery_exception() : std::runtime_error("cannot load scenery") {}
};
#endif

View File

@@ -1772,11 +1772,10 @@ void TController::Activation()
}
if (pVehicle != old)
{ // jeśli zmieniony został pojazd prowadzony
if( ( simulation::Train )
&& ( simulation::Train->Dynamic() == old ) ) {
// ewentualna zmiana kabiny użytkownikowi
Global.changeDynObj = pVehicle; // uruchomienie protezy
}
// ewentualna zmiana kabiny TTrain
TTrain *train = simulation::Trains.find(old->name());
if (train)
train->MoveToVehicle(pVehicle);
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu (może się zmienić) -
// silnikowy dla EZT
}

View File

@@ -2330,17 +2330,6 @@ TDynamicObject::create_controller( std::string const Type, bool const Trainset )
if( Type == "" ) { return; }
if( asName == Global.asHumanCtrlVehicle ) {
// jeśli pojazd wybrany do prowadzenia
if( MoverParameters->EngineType != TEngineType::Dumb ) {
// wsadzamy tam sterującego
Controller = Humandriver;
}
else {
// w przeciwnym razie trzeba włączyć pokazywanie kabiny
bDisplayCab = true;
}
}
// McZapkie-151102: rozkład jazdy czytany z pliku *.txt z katalogu w którym jest sceneria
if( ( Type == "1" )
|| ( Type == "2" ) ) {

View File

@@ -52,7 +52,7 @@ global_settings::ConfigParse(cParser &Parser) {
{
Parser.getTokens();
Parser >> asHumanCtrlVehicle;
Parser >> local_start_vehicle;
}
else if( token == "fieldofview" ) {

View File

@@ -30,7 +30,6 @@ struct global_settings {
std::mt19937 local_random_engine;
bool ready_to_load { false };
uint32_t random_seed;
TDynamicObject *changeDynObj{ nullptr };// info o zmianie pojazdu
TCamera pCamera; // parametry kamery
TCamera pDebugCamera;
std::array<Math3D::vector3, 10> FreeCameraInit; // pozycje kamery
@@ -61,7 +60,7 @@ struct global_settings {
std::string szTexturesDDS{ ".dds" }; // lista tekstur od DDS
std::string szDefaultExt{ szTexturesDDS };
std::string SceneryFile{ "td.scn" };
std::string asHumanCtrlVehicle{ "EU07-424" };
std::string local_start_vehicle{ "EU07-424" };
int iConvertModels{ 0 }; // tworzenie plików binarnych
// logs
int iWriteLogEnabled{ 3 }; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów

View File

@@ -46,12 +46,13 @@ std::string filename_date() {
std::snprintf(
logbuffer,
sizeof(logbuffer),
"%d%02d%02d_%02d%02d",
"%d%02d%02d_%02d%02d%03d",
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute );
st.wMinute,
st.wMilliseconds);
return std::string( logbuffer );
}

10
Names.h
View File

@@ -46,8 +46,16 @@ public:
auto lookup = m_itemmap.find( Name );
if (lookup == m_itemmap.end())
return;
delete m_items[lookup->second];
detach(Name);
}
void detach (std::string const &Name)
{
auto lookup = m_itemmap.find( Name );
if (lookup == m_itemmap.end())
return;
m_items[lookup->second] = nullptr;
// TBD, TODO: remove from m_items?

View File

@@ -4749,11 +4749,12 @@ void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Comm
if( false == Train->CabChange( 1 ) ) {
if( TestFlag( Train->DynamicObject->MoverParameters->Couplers[ side::front ].CouplingFlag, coupling::gangway ) ) {
// przejscie do nastepnego pojazdu
Global.changeDynObj = Train->DynamicObject->PrevConnected;
Global.changeDynObj->MoverParameters->ActiveCab = (
TDynamicObject *dynobj = Train->DynamicObject->PrevConnected;
dynobj->MoverParameters->ActiveCab = (
Train->DynamicObject->PrevConnectedNo ?
-1 :
1 );
Train->MoveToVehicle(dynobj);
}
}
}
@@ -4765,11 +4766,12 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com
if( false == Train->CabChange( -1 ) ) {
if( TestFlag( Train->DynamicObject->MoverParameters->Couplers[ side::rear ].CouplingFlag, coupling::gangway ) ) {
// przejscie do nastepnego pojazdu
Global.changeDynObj = Train->DynamicObject->NextConnected;
Global.changeDynObj->MoverParameters->ActiveCab = (
TDynamicObject *dynobj = Train->DynamicObject->NextConnected;
dynobj->MoverParameters->ActiveCab = (
Train->DynamicObject->NextConnectedNo ?
-1 :
1 );
Train->MoveToVehicle(dynobj);
}
}
}
@@ -6911,6 +6913,58 @@ void TTrain::DynamicSet(TDynamicObject *d)
}
};
void
TTrain::MoveToVehicle(TDynamicObject *target) {
ErrorLog("MoveToVehicle");
// > Ra: to nie może być tak robione, to zbytnia proteza jest
// indeed, too much hacks...
simulation::Trains.detach(Dynamic()->name());
if( Dynamic()->Mechanik ) {
// AI może sobie samo pójść
if( false == Dynamic()->Mechanik->AIControllFlag ) {
// tylko jeśli ręcznie prowadzony
// jeśli prowadzi AI, to mu nie robimy dywersji!
Occupied()->CabDeactivisation();
Occupied()->ActiveCab = 0;
Occupied()->BrakeLevelSet( Occupied()->Handle->GetPos( bh_NP ) ); //rozwala sterowanie hamulcem GF 04-2016
Dynamic()->MechInside = false;
Dynamic()->Controller = AIdriver;
}
}
Dynamic()->bDisplayCab = false;
Dynamic()->ABuSetModelShake( {} );
if( Dynamic()->Mechanik ) // AI może sobie samo pójść
if( false == Dynamic()->Mechanik->AIControllFlag ) {
// tylko jeśli ręcznie prowadzony
// przsunięcie obiektu zarządzającego
Dynamic()->Mechanik->MoveTo( target );
}
DynamicSet( target );
if( Dynamic()->Mechanik ) // AI może sobie samo pójść
if( false == Dynamic()->Mechanik->AIControllFlag ) // tylko jeśli ręcznie prowadzony
{
Occupied()->LimPipePress = Occupied()->PipePress;
Occupied()->CabActivisation(); // załączenie rozrządu (wirtualne kabiny)
Dynamic()->MechInside = true;
Dynamic()->Controller = Humandriver;
}
InitializeCab(
Occupied()->CabNo,
Dynamic()->asBaseDir + Occupied()->TypeName + ".mmd" );
if( false == FreeFlyModeFlag ) {
Dynamic()->bDisplayCab = true;
Dynamic()->ABuSetModelShake( {} ); // zerowanie przesunięcia przed powrotem?
}
simulation::Trains.insert(this, Dynamic()->name());
}
// checks whether specified point is within boundaries of the active cab
bool
TTrain::point_inside( Math3D::vector3 const Point ) const {

View File

@@ -670,6 +670,7 @@ private:
inline TMoverParameters *Occupied() { return mvOccupied; };
inline TMoverParameters const *Occupied() const { return mvOccupied; };
void DynamicSet(TDynamicObject *d);
void MoveToVehicle(TDynamicObject *target);
// checks whether specified point is within boundaries of the active cab
bool point_inside( Math3D::vector3 const Point ) const;
Math3D::vector3 clamp_inside( Math3D::vector3 const &Point ) const;

View File

@@ -9,9 +9,9 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "application.h"
#include "scenarioloadermode.h"
#include "drivermode.h"
#include "editormode.h"
#include "scenarioloadermode.h"
#include "Globals.h"
#include "simulation.h"
@@ -104,20 +104,20 @@ eu07_application::init( int Argc, char *Argv[] ) {
int result { 0 };
WriteLog( "Starting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")" );
WriteLog( "For online documentation and additional files refer to: http://eu07.pl" );
WriteLog( "Authors: Marcin_EU, McZapkie, ABu, Winger, Tolaris, nbmx, OLO_EU, Bart, Quark-t, "
"ShaXbee, Oli_EU, youBy, KURS90, Ra, hunter, szociu, Stele, Q, firleju and others\n" );
init_debug();
init_files();
if( ( result = init_settings( Argc, Argv ) ) != 0 ) {
return result;
}
WriteLog( "Starting MaSzyna rail vehicle simulator (release: " + Global.asVersion + ")" );
WriteLog( "For online documentation and additional files refer to: http://eu07.pl" );
WriteLog( "Authors: Marcin_EU, McZapkie, ABu, Winger, Tolaris, nbmx, OLO_EU, Bart, Quark-t, "
"ShaXbee, Oli_EU, youBy, KURS90, Ra, hunter, szociu, Stele, Q, firleju and others\n" );
if( ( result = init_locale() ) != 0 ) {
return result;
}
if( ( result = init_glfw() ) != 0 ) {
return result;
}
@@ -171,81 +171,94 @@ eu07_application::run() {
double frameStartTime = Timer::GetTime();
bool nextloop = true;
while (nextloop)
{
command_queue::commands_map commands_to_exec;
command_queue::commands_map local_commands = simulation::Commands.pop_intercept_queue();
double slave_sync;
if (m_modes[m_modestack.top()]->is_command_processor()) {
// active mode is doing real calculations (e.g. drivermode)
// if we're the server
if (m_network && m_network->servers)
bool nextloop = true;
while (nextloop)
{
// fetch from network layer command requests received from clients
command_queue::commands_map remote_commands = m_network->servers->pop_commands();
command_queue::commands_map commands_to_exec;
command_queue::commands_map local_commands = simulation::Commands.pop_intercept_queue();
double slave_sync;
// push these into local queue
add_to_dequemap(local_commands, remote_commands);
}
// if we're the server
if (m_network && m_network->servers)
{
// fetch from network layer command requests received from clients
command_queue::commands_map remote_commands = m_network->servers->pop_commands();
// if we're slave
if (m_network && m_network->client)
{
// fetch frame info from network layer,
auto frame_info = m_network->client->get_next_delta();
// push these into local queue
add_to_dequemap(local_commands, remote_commands);
}
// use delta and commands received from master
double delta = std::get<0>(frame_info);
Timer::set_delta_override(delta);
slave_sync = std::get<1>(frame_info);
add_to_dequemap(commands_to_exec, std::get<2>(frame_info));
// if we're slave
if (m_network && m_network->client)
{
// fetch frame info from network layer,
auto frame_info = m_network->client->get_next_delta();
// and send our local commands to master
m_network->client->send_commands(local_commands);
// use delta and commands received from master
double delta = std::get<0>(frame_info);
Timer::set_delta_override(delta);
slave_sync = std::get<1>(frame_info);
add_to_dequemap(commands_to_exec, std::get<2>(frame_info));
// and send our local commands to master
m_network->client->send_commands(local_commands);
if (delta == 0.0)
nextloop = false;
}
// if we're master
else {
// just push local commands to execution
add_to_dequemap(commands_to_exec, local_commands);
if (delta == 0.0)
nextloop = false;
}
// if we're master
else {
// just push local commands to execution
add_to_dequemap(commands_to_exec, local_commands);
}
nextloop = false;
}
// send commands to command queue
simulation::Commands.push_commands(commands_to_exec);
// send commands to command queue
simulation::Commands.push_commands(commands_to_exec);
// do actual frame processing
if (!m_modes[ m_modestack.top() ]->update())
goto die;
// update continuous commands
simulation::Commands.update();
double sync = generate_sync();
// if we're the server
if (m_network && m_network->servers)
{
// send delta, sync, and commands we just executed to clients
double delta = Timer::GetDeltaTime();
m_network->servers->push_delta(delta, sync, commands_to_exec);
}
// if we're slave
if (m_network && m_network->client)
{
// verify sync
if (sync != slave_sync) {
WriteLog("net: DESYNC!", logtype::net);
}
// set total delta for rendering code
double totalDelta = Timer::GetTime() - frameStartTime;
Timer::set_delta_override(totalDelta);
}
}
} else {
// active mode is loader
// clear local command queue
simulation::Commands.pop_intercept_queue();
// do actual frame processing
if (!m_modes[ m_modestack.top() ]->update())
goto die;
// update continuous commands
simulation::Commands.update();
double sync = generate_sync();
// if we're the server
if (m_network && m_network->servers)
{
// send delta, sync, and commands we just executed to clients
double delta = Timer::GetDeltaTime();
m_network->servers->push_delta(delta, sync, commands_to_exec);
}
// if we're slave
if (m_network && m_network->client)
{
// verify sync
if (sync != slave_sync) {
WriteLog("net: DESYNC!", logtype::net);
}
// set total delta for rendering code
double totalDelta = Timer::GetTime() - frameStartTime;
Timer::set_delta_override(totalDelta);
}
}
// -------------------------------------------------------------------
@@ -269,7 +282,7 @@ eu07_application::run() {
}
if (m_network)
m_network->poll();
m_network->update();
auto frametime = Timer::subsystem.mainloop_total.stop();
if (Global.minframetime.count() != 0.0f && (Global.minframetime - frametime).count() > 0.0f)
@@ -490,7 +503,7 @@ eu07_application::init_files() {
#elif __linux__
unlink("log.txt");
unlink("errors.txt");
mkdir("logs", 0664);
mkdir("logs", 0755);
#endif
}
@@ -519,7 +532,7 @@ eu07_application::init_settings( int Argc, char *Argv[] ) {
}
else if( token == "-v" ) {
if( i + 1 < Argc ) {
Global.asHumanCtrlVehicle = ToLower( Argv[ ++i ] );
Global.local_start_vehicle = ToLower( Argv[ ++i ] );
}
}
else {
@@ -709,6 +722,11 @@ bool eu07_application::init_network() {
if (Global.network_conf.is_client) {
m_network->connect(Global.network_conf.client_host, Global.network_conf.client_port);
}
else {
Global.random_seed = std::random_device{}();
Global.random_engine.seed(Global.random_seed);
Global.ready_to_load = true;
}
}
return true;

View File

@@ -63,6 +63,9 @@ public:
virtual
void
on_event_poll() = 0;
virtual
bool
is_command_processor() = 0;
protected:
// members

View File

@@ -197,9 +197,9 @@ driver_mode::update() {
InOutKey();
}
if( Global.changeDynObj ) {
// ABu zmiana pojazdu - przejście do innego
ChangeDynamic();
if (!FreeFlyModeFlag && simulation::Train->Dynamic() != Camera.m_owner) {
// fixup camera after vehicle switch
CabView();
}
if( simulation::Train != nullptr )
@@ -264,8 +264,8 @@ void
driver_mode::enter() {
TDynamicObject *nPlayerTrain { (
( Global.asHumanCtrlVehicle != "ghostview" ) ?
simulation::Vehicles.find( Global.asHumanCtrlVehicle ) :
( Global.local_start_vehicle != "ghostview" ) ?
simulation::Vehicles.find( Global.local_start_vehicle ) :
nullptr ) };
Camera.Init(Global.FreeCameraInit[0], Global.FreeCameraInitAngle[0], nullptr );
@@ -277,14 +277,14 @@ driver_mode::enter() {
if (nPlayerTrain)
{
WriteLog( "Trying to enter player train, \"" + Global.asHumanCtrlVehicle + "\"" );
WriteLog( "Trying to enter player train, \"" + Global.local_start_vehicle + "\"" );
m_relay.post(user_command::entervehicle, 0.0, 0.0, GLFW_PRESS, 0, nPlayerTrain->GetPosition());
change_train = nPlayerTrain->name();
}
else if (Global.asHumanCtrlVehicle != "ghostview")
else if (Global.local_start_vehicle != "ghostview")
{
Error("Bad scenario: failed to locate player train, \"" + Global.asHumanCtrlVehicle + "\"" );
Error("Bad scenario: failed to locate player train, \"" + Global.local_start_vehicle + "\"" );
}
// if (!Global.bMultiplayer) //na razie włączone
@@ -301,7 +301,7 @@ driver_mode::enter() {
KeyEvents[ 9 ] = simulation::Events.FindEvent( "keyctrl09" );
}
Timer::ResetTimers();
Timer::ResetTimers();
set_picking( Global.ControlPicking );
}
@@ -377,6 +377,12 @@ driver_mode::on_event_poll() {
m_input.poll();
}
bool
driver_mode::is_command_processor() {
return true;
}
void
driver_mode::update_camera( double const Deltatime ) {
@@ -946,65 +952,6 @@ driver_mode::CabView() {
train->pMechOffset = Camera.m_owneroffset;
}
void
driver_mode::ChangeDynamic() {
auto *train { simulation::Train };
if( train == nullptr ) { return; }
auto *vehicle { train->Dynamic() };
auto *occupied { train->Occupied() };
auto *driver { vehicle->Mechanik };
// Ra: to nie może być tak robione, to zbytnia proteza jest
if( driver ) {
// AI może sobie samo pójść
if( false == driver->AIControllFlag ) {
// tylko jeśli ręcznie prowadzony
// jeśli prowadzi AI, to mu nie robimy dywersji!
occupied->CabDeactivisation();
occupied->ActiveCab = 0;
occupied->BrakeLevelSet( occupied->Handle->GetPos( bh_NP ) ); //rozwala sterowanie hamulcem GF 04-2016
vehicle->MechInside = false;
vehicle->Controller = AIdriver;
}
}
TDynamicObject *temp = Global.changeDynObj;
vehicle->bDisplayCab = false;
vehicle->ABuSetModelShake( {} );
if( driver ) // AI może sobie samo pójść
if( false == driver->AIControllFlag ) {
// tylko jeśli ręcznie prowadzony
// przsunięcie obiektu zarządzającego
driver->MoveTo( temp );
}
train->DynamicSet( temp );
// update helpers
train = simulation::Train;
vehicle = train->Dynamic();
occupied = train->Occupied();
driver = vehicle->Mechanik;
Global.asHumanCtrlVehicle = vehicle->name();
if( driver ) // AI może sobie samo pójść
if( false == driver->AIControllFlag ) // tylko jeśli ręcznie prowadzony
{
occupied->LimPipePress = occupied->PipePress;
occupied->CabActivisation(); // załączenie rozrządu (wirtualne kabiny)
vehicle->MechInside = true;
vehicle->Controller = Humandriver;
}
train->InitializeCab(
occupied->CabNo,
vehicle->asBaseDir + occupied->TypeName + ".mmd" );
if( false == FreeFlyModeFlag ) {
vehicle->bDisplayCab = true;
vehicle->ABuSetModelShake( {} ); // zerowanie przesunięcia przed powrotem?
CabView(); // na pozycję mecha
}
Global.changeDynObj = nullptr;
}
void
driver_mode::InOutKey()
{ // przełączenie widoku z kabiny na zewnętrzny i odwrotnie

View File

@@ -49,6 +49,8 @@ public:
on_scroll( double const Xoffset, double const Yoffset ) override;
void
on_event_poll() override;
bool
is_command_processor() override;
private:
// types
@@ -86,7 +88,7 @@ private:
void update_camera( const double Deltatime );
// handles vehicle change flag
void OnKeyDown( int cKey );
void ChangeDynamic();
void InOutKey();
void CabView();
void ExternalView();
@@ -99,7 +101,7 @@ private:
TCamera Camera;
TCamera DebugCamera;
int m_externalviewmode { view::consistfront }; // selected external view mode
bool m_externalview { false };
bool m_externalview { true };
std::array<view_config, view::count_> m_externalviewconfigs;
TDynamicObject *pDynamicNearest { nullptr }; // vehicle nearest to the active camera. TODO: move to camera
double fTime50Hz { 0.0 }; // bufor czasu dla komunikacji z PoKeys

View File

@@ -262,6 +262,11 @@ editor_mode::on_event_poll() {
m_input.poll();
}
bool editor_mode::is_command_processor() {
return false;
}
bool
editor_mode::mode_translation() const {

View File

@@ -45,6 +45,8 @@ public:
on_scroll( double const Xoffset, double const Yoffset ) override { ; }
void
on_event_poll() override;
bool
is_command_processor() override;
private:
// types

View File

@@ -140,7 +140,7 @@ OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
// jeśli długość nazwy jest niezerowa szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #7
auto *vehicle = (
pRozkaz->cString[ 1 ] == '*' ?
simulation::Vehicles.find( Global.asHumanCtrlVehicle ) :
simulation::Train->Dynamic() :
simulation::Vehicles.find( std::string{ pRozkaz->cString + 1, (unsigned)pRozkaz->cString[ 0 ] } ) );
if( vehicle != nullptr ) {
WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe
@@ -181,7 +181,7 @@ OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
{ // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13
auto *lookup = (
pRozkaz->cString[ 2 ] == '*' ?
simulation::Vehicles.find( Global.asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika
simulation::Train->Dynamic() : // nazwa pojazdu użytkownika
simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu
if( lookup == nullptr ) { break; } // nothing found, nothing to do
auto *d { lookup };

View File

@@ -9,10 +9,14 @@ network::tcp::connection::connection(asio::io_context &io_ctx, bool client, size
m_header_buffer.resize(8);
}
network::tcp::connection::~connection()
{
m_socket.close();
}
void network::tcp::connection::disconnect()
{
network::connection::disconnect();
m_socket.close();
}
void network::tcp::connection::send_data(std::shared_ptr<std::string> buffer)
@@ -176,8 +180,6 @@ void network::tcp::client::connect()
if (this->conn)
return;
std::cout << "create sock" << std::endl;
std::shared_ptr<connection> conn = std::make_shared<connection>(io_ctx, true, resume_frame_counter);
conn->set_handler(std::bind(&client::handle_message, this, conn, std::placeholders::_1));
@@ -187,7 +189,6 @@ void network::tcp::client::connect()
conn->m_socket.set_option(asio::ip::tcp::no_delay(true));
conn->m_socket.async_connect(endpoint,
std::bind(&client::handle_accept, this, std::placeholders::_1));
std::cout << "connect start" <<std::endl;
this->conn = conn;
@@ -195,7 +196,6 @@ void network::tcp::client::connect()
void network::tcp::client::handle_accept(const asio::error_code &err)
{
std::cout << "connect handl" <<std::endl;
if (!err)
{
conn->connected();
@@ -203,6 +203,6 @@ void network::tcp::client::handle_accept(const asio::error_code &err)
else
{
WriteLog(std::string("net: failed to connect: " + err.message()), logtype::net);
conn->state = connection::DEAD;
conn->disconnect();
}
}

View File

@@ -14,6 +14,7 @@ namespace network::tcp
public:
connection(asio::io_context &io_ctx, bool client = false, size_t counter = 0);
~connection();
virtual void connected() override;
virtual void disconnect() override;

View File

@@ -22,14 +22,14 @@ void network::server_manager::push_delta(double dt, double sync, const command_q
if (dt == 0.0 && commands.empty())
return;
for (auto srv : servers)
srv->push_delta(dt, sync, commands);
frame_info msg;
msg.dt = dt;
msg.sync = sync;
msg.commands = commands;
for (auto srv : servers)
srv->push_delta(msg);
serialize_message(msg, *backbuffer.get());
}
@@ -42,10 +42,13 @@ network::manager::manager()
{
}
void network::manager::poll()
void network::manager::update()
{
io_context.restart();
io_context.poll();
if (client)
client->update();
}
void network::manager::create_server(const std::string &host, uint32_t port)

View File

@@ -32,6 +32,6 @@ namespace network
void create_server(const std::string &host, uint32_t port);
void connect(const std::string &host, uint32_t port);
void poll();
void update();
};
}

View File

@@ -40,27 +40,29 @@ void network::connection::catch_up()
std::vector<std::shared_ptr<message>> messages;
for (size_t i = 0; i < CATCHUP_PACKETS; i++) {
for (int i = 0; i < CATCHUP_PACKETS; i++) {
if (backbuffer->peek() == EOF) {
send_messages(messages);
backbuffer->seekg(0, std::ios_base::end);
state = ACTIVE;
backbuffer->seekg(0, std::ios_base::end);
return;
}
auto msg = deserialize_message(*backbuffer.get());
if (packet_counter) {
packet_counter--;
i--; // TODO: it would be better to skip frames in chunks
continue;
}
messages.push_back(deserialize_message(*backbuffer.get()));
messages.push_back(msg);
}
send_messages(messages);
backbuffer_pos = backbuffer->tellg();
backbuffer->seekg(0, std::ios_base::end);
send_messages(messages);
}
void network::connection::send_complete(std::shared_ptr<std::string> buf)
@@ -108,13 +110,8 @@ network::server::server(std::shared_ptr<std::istream> buf) : backbuffer(buf)
}
void network::server::push_delta(double dt, double sync, const command_queue::commands_map &commands)
void network::server::push_delta(const frame_info &msg)
{
frame_info msg;
msg.dt = dt;
msg.sync = sync;
msg.commands = commands;
for (auto it = clients.begin(); it != clients.end(); ) {
if ((*it)->state == connection::DEAD) {
it = clients.erase(it);
@@ -146,6 +143,11 @@ void network::server::handle_message(std::shared_ptr<connection> conn, const mes
if (msg.type == message::CLIENT_HELLO) {
auto cmd = dynamic_cast<const client_hello&>(msg);
if (cmd.version != 1) {
conn->disconnect();
return;
}
server_hello reply;
reply.seed = Global.random_seed;
conn->state = connection::CATCHING_UP;
@@ -167,20 +169,24 @@ void network::server::handle_message(std::shared_ptr<connection> conn, const mes
// ------------
// client
int zzz = 20;
std::tuple<double, double, command_queue::commands_map> network::client::get_next_delta()
void network::client::update()
{
if (conn && conn->state == connection::DEAD) {
conn.reset();
}
if (!conn) {
zzz--;
if (zzz < 0)
if (!reconnect_delay) {
connect();
reconnect_delay = RECONNECT_DELAY_FRAMES;
}
reconnect_delay--;
}
}
// client
std::tuple<double, double, command_queue::commands_map> network::client::get_next_delta()
{
if (delta_queue.empty()) {
return std::tuple<double, double,
command_queue::commands_map>(0.0, 0.0, command_queue::commands_map());
@@ -189,6 +195,7 @@ std::tuple<double, double, command_queue::commands_map> network::client::get_nex
auto entry = delta_queue.front();
delta_queue.pop();
auto &delta = entry.second;
return std::make_tuple(entry.second.dt, entry.second.sync, entry.second.commands);
}
@@ -216,9 +223,11 @@ void network::client::handle_message(std::shared_ptr<connection> conn, const mes
auto cmd = dynamic_cast<const server_hello&>(msg);
conn->state = connection::ACTIVE;
Global.random_seed = cmd.seed;
Global.random_engine.seed(Global.random_seed);
Global.ready_to_load = true;
if (!Global.ready_to_load) {
Global.random_seed = cmd.seed;
Global.random_engine.seed(Global.random_seed);
Global.ready_to_load = true;
}
WriteLog("net: accept received", logtype::net);
}

View File

@@ -16,7 +16,7 @@ namespace network
friend class client;
private:
const size_t CATCHUP_PACKETS = 100;
const int CATCHUP_PACKETS = 100;
/*
std::queue<
@@ -74,7 +74,7 @@ namespace network
public:
server(std::shared_ptr<std::istream> buf);
void push_delta(double dt, double sync, const command_queue::commands_map &commands);
void push_delta(const frame_info &msg);
command_queue::commands_map pop_commands();
};
@@ -85,12 +85,16 @@ namespace network
void handle_message(std::shared_ptr<connection> conn, const message &msg);
std::shared_ptr<connection> conn;
size_t resume_frame_counter = 0;
size_t reconnect_delay = 0;
const size_t RECONNECT_DELAY_FRAMES = 60;
std::queue<
std::pair<std::chrono::high_resolution_clock::time_point,
frame_info>> delta_queue;
public:
void update();
std::tuple<double, double, command_queue::commands_map> get_next_delta();
void send_commands(command_queue::commands_map commands);
};

View File

@@ -2114,9 +2114,6 @@ bool opengl_renderer::Render(TDynamicObject *Dynamic)
m_sunlight.apply_intensity(Dynamic->fShade);
}
// calc_motion = true;
// std::cout << glm::to_string(glm::vec3(Dynamic->get_velocity_vec())) << std::endl;
// render
if (Dynamic->mdLowPolyInt)
Render(Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance);
@@ -2128,7 +2125,6 @@ bool opengl_renderer::Render(TDynamicObject *Dynamic)
Render(Dynamic->mdLoad, Dynamic->Material(), squaredistance, {0.f, Dynamic->LoadOffset, 0.f}, {});
// post-render cleanup
// calc_motion = false;
if (Dynamic->fShade > 0.0f)
{

View File

@@ -35,27 +35,40 @@ scenarioloader_mode::init() {
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
scenarioloader_mode::update() {
if (!Global.ready_to_load)
// waiting for network connection
return true;
WriteLog( "\nLoading scenario \"" + Global.SceneryFile + "\"..." );
if (!state) {
WriteLog( "\nLoading scenario \"" + Global.SceneryFile + "\"..." );
auto timestart = std::chrono::system_clock::now();
timestart = std::chrono::system_clock::now();
state = simulation::State.deserialize_begin(Global.SceneryFile);
}
if( true == simulation::State.deserialize( Global.SceneryFile ) ) {
WriteLog( "Scenario loading time: " + std::to_string( std::chrono::duration_cast<std::chrono::seconds>( ( std::chrono::system_clock::now() - timestart ) ).count() ) + " seconds" );
// TODO: implement and use next mode cue
Global.random_seed = std::random_device{}();
Global.random_engine.seed(Global.random_seed);
Application.pop_mode();
Application.push_mode( eu07_application::mode::driver );
}
else {
ErrorLog( "Bad init: scenario loading failed" );
Application.pop_mode();
}
try {
if (simulation::State.deserialize_continue(state))
return true;
}
catch (invalid_scenery_exception &e) {
ErrorLog( "Bad init: scenario loading failed" );
Application.pop_mode();
}
WriteLog( "Scenario loading time: " + std::to_string( std::chrono::duration_cast<std::chrono::seconds>( ( std::chrono::system_clock::now() - timestart ) ).count() ) + " seconds" );
// TODO: implement and use next mode cue
Application.pop_mode();
Application.push_mode( eu07_application::mode::driver );
return true;
}
bool
scenarioloader_mode::is_command_processor() {
return false;
}
// maintenance method, called when the mode is activated
void
scenarioloader_mode::enter() {

View File

@@ -10,8 +10,11 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include "applicationmode.h"
#include "simulation.h"
class scenarioloader_mode : public application_mode {
std::shared_ptr<simulation::deserializer_state> state;
std::chrono::system_clock::time_point timestart;
public:
// constructors
@@ -40,4 +43,6 @@ public:
on_scroll( double const Xoffset, double const Yoffset ) override { ; }
void
on_event_poll() override { ; }
bool
is_command_processor() override;
};

View File

@@ -45,10 +45,16 @@ TTrain *Train { nullptr };
uint16_t prev_train_id { 0 };
bool is_ready { false };
bool
state_manager::deserialize( std::string const &Scenariofile ) {
std::shared_ptr<deserializer_state>
state_manager::deserialize_begin(std::string const &Scenariofile) {
return m_serializer.deserialize( Scenariofile );
return m_serializer.deserialize_begin( Scenariofile );
}
bool
state_manager::deserialize_continue(std::shared_ptr<deserializer_state> state) {
return m_serializer.deserialize_continue(state);
}
// stores class data in specified file, in legacy (text) format

View File

@@ -26,9 +26,12 @@ public:
update( double Deltatime, int Iterationcount );
void
update_clocks();
// restores simulation data from specified file. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// starts deserialization from specified file, returns context pointer on success, throws otherwise
std::shared_ptr<deserializer_state>
deserialize_begin(std::string const &Scenariofile);
// continues deserialization for given context, amount limited by time, returns true if needs to be called again
bool
deserialize_continue(std::shared_ptr<deserializer_state> state);
// stores class data in specified file, in legacy (text) format
void
export_as_text( std::string const &Scenariofile ) const;
@@ -63,6 +66,8 @@ extern TTrain *Train;
extern uint16_t prev_train_id;
extern bool is_ready;
class deserializer_state;
} // simulation
//---------------------------------------------------------------------------

View File

@@ -25,98 +25,87 @@ http://mozilla.org/MPL/2.0/.
namespace simulation {
bool
state_serializer::deserialize( std::string const &Scenariofile ) {
std::shared_ptr<deserializer_state>
state_serializer::deserialize_begin( std::string const &Scenariofile ) {
// TODO: move initialization to separate routine so we can reuse it
SafeDelete( Region );
Region = new scene::basic_region();
// NOTE: for the time being import from text format is a given, since we don't have full binary serialization
std::shared_ptr<deserializer_state> state =
std::make_shared<deserializer_state>(Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction);
// TODO: check first for presence of serialized binary files
// if this fails, fall back on the legacy text format
scene::scratch_data importscratchpad;
importscratchpad.name = Scenariofile;
state->scratchpad.name = Scenariofile;
if( Scenariofile != "$.scn" ) {
// compilation to binary file isn't supported for rainsted-created overrides
// NOTE: we postpone actual loading of the scene until we process time, season and weather data
importscratchpad.binary.terrain = Region->is_scene( Scenariofile ) ;
state->scratchpad.binary.terrain = Region->is_scene( Scenariofile ) ;
}
// NOTE: for the time being import from text format is a given, since we don't have full binary serialization
cParser scenarioparser( Scenariofile, cParser::buffer_FILE, Global.asCurrentSceneryPath, Global.bLoadTraction );
if( false == scenarioparser.ok() ) { return false; }
if( false == state->input.ok() )
throw invalid_scenery_exception();
deserialize( scenarioparser, importscratchpad );
if( ( false == importscratchpad.binary.terrain )
&& ( Scenariofile != "$.scn" ) ) {
// if we didn't find usable binary version of the scenario files, create them now for future use
// as long as the scenario file wasn't rainsted-created base file override
Region->serialize( Scenariofile );
}
return true;
// prepare deserialization function table
// since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task
using deserializefunction = void( state_serializer::*)(cParser &, scene::scratch_data &);
std::vector<
std::pair<
std::string,
deserializefunction> > functionlist = {
{ "area", &state_serializer::deserialize_area },
{ "atmo", &state_serializer::deserialize_atmo },
{ "camera", &state_serializer::deserialize_camera },
{ "config", &state_serializer::deserialize_config },
{ "description", &state_serializer::deserialize_description },
{ "event", &state_serializer::deserialize_event },
{ "lua", &state_serializer::deserialize_lua },
{ "firstinit", &state_serializer::deserialize_firstinit },
{ "group", &state_serializer::deserialize_group },
{ "endgroup", &state_serializer::deserialize_endgroup },
{ "light", &state_serializer::deserialize_light },
{ "node", &state_serializer::deserialize_node },
{ "origin", &state_serializer::deserialize_origin },
{ "endorigin", &state_serializer::deserialize_endorigin },
{ "rotate", &state_serializer::deserialize_rotate },
{ "sky", &state_serializer::deserialize_sky },
{ "test", &state_serializer::deserialize_test },
{ "time", &state_serializer::deserialize_time },
{ "trainset", &state_serializer::deserialize_trainset },
{ "endtrainset", &state_serializer::deserialize_endtrainset } };
for( auto &function : functionlist ) {
state->functionmap.emplace( function.first, std::bind( function.second, this, std::ref( state->input ), std::ref( state->scratchpad ) ) );
}
return state;
}
// restores class data from provided stream
void
state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) {
// prepare deserialization function table
// since all methods use the same objects, we can have simple, hard-coded binds or lambdas for the task
using deserializefunction = void( state_serializer::*)(cParser &, scene::scratch_data &);
std::vector<
std::pair<
std::string,
deserializefunction> > functionlist = {
{ "area", &state_serializer::deserialize_area },
{ "atmo", &state_serializer::deserialize_atmo },
{ "camera", &state_serializer::deserialize_camera },
{ "config", &state_serializer::deserialize_config },
{ "description", &state_serializer::deserialize_description },
{ "event", &state_serializer::deserialize_event },
{ "lua", &state_serializer::deserialize_lua },
{ "firstinit", &state_serializer::deserialize_firstinit },
{ "group", &state_serializer::deserialize_group },
{ "endgroup", &state_serializer::deserialize_endgroup },
{ "light", &state_serializer::deserialize_light },
{ "node", &state_serializer::deserialize_node },
{ "origin", &state_serializer::deserialize_origin },
{ "endorigin", &state_serializer::deserialize_endorigin },
{ "rotate", &state_serializer::deserialize_rotate },
{ "sky", &state_serializer::deserialize_sky },
{ "test", &state_serializer::deserialize_test },
{ "time", &state_serializer::deserialize_time },
{ "trainset", &state_serializer::deserialize_trainset },
{ "endtrainset", &state_serializer::deserialize_endtrainset } };
using deserializefunctionbind = std::function<void()>;
std::unordered_map<
std::string,
deserializefunctionbind> functionmap;
for( auto &function : functionlist ) {
functionmap.emplace( function.first, std::bind( function.second, this, std::ref( Input ), std::ref( Scratchpad ) ) );
}
// continues deserialization for given context, amount limited by time, returns true if needs to be called again
bool
state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state) {
cParser &Input = state->input;
scene::scratch_data &Scratchpad = state->scratchpad;
// deserialize content from the provided input
auto
timelast { std::chrono::steady_clock::now() },
timenow { timelast };
auto timelast { std::chrono::steady_clock::now() };
std::string token { Input.getToken<std::string>() };
while( false == token.empty() ) {
auto lookup = functionmap.find( token );
if( lookup != functionmap.end() ) {
auto lookup = state->functionmap.find( token );
if( lookup != state->functionmap.end() ) {
lookup->second();
}
else {
ErrorLog( "Bad scenario: unexpected token \"" + token + "\" defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
}
timenow = std::chrono::steady_clock::now();
auto timenow = std::chrono::steady_clock::now();
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 200 ) {
timelast = timenow;
glfwPollEvents();
Application.set_progress( Input.getProgress(), Input.getFullProgress() );
GfxRenderer.Render();
GfxRenderer.SwapBuffers();
return true;
}
token = Input.getToken<std::string>();
@@ -129,6 +118,15 @@ state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad )
if (Global.map_enabled)
Region->create_map_geometry();
if( ( false == state->scratchpad.binary.terrain )
&& ( state->scenariofile != "$.scn" ) ) {
// if we didn't find usable binary version of the scenario files, create them now for future use
// as long as the scenario file wasn't rainsted-created base file override
Region->serialize( state->scenariofile );
}
return false;
}
void

View File

@@ -14,21 +14,37 @@ http://mozilla.org/MPL/2.0/.
namespace simulation {
struct deserializer_state {
std::string scenariofile;
cParser input;
scene::scratch_data scratchpad;
using deserializefunctionbind = std::function<void()>;
std::unordered_map<
std::string,
deserializefunctionbind> functionmap;
deserializer_state(std::string const &File, cParser::buffertype const Type, const std::string &Path, bool const Loadtraction)
: scenariofile(File), input(File, Type, Path, Loadtraction) { }
};
class state_serializer {
public:
// methods
// restores simulation data from specified file. returns: true on success, false otherwise
bool
deserialize( std::string const &Scenariofile );
// starts deserialization from specified file, returns context pointer on success, throws otherwise
std::shared_ptr<deserializer_state>
deserialize_begin(std::string const &Scenariofile);
// continues deserialization for given context, amount limited by time, returns true if needs to be called again
bool
deserialize_continue(std::shared_ptr<deserializer_state> state);
// stores class data in specified file, in legacy (text) format
void
export_as_text( std::string const &Scenariofile ) const;
private:
// methods
// restores class data from provided stream
void deserialize( cParser &Input, scene::scratch_data &Scratchpad );
// restores class data from provided stream
void deserialize_area( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad );