This commit is contained in:
milek7
2019-01-01 00:26:11 +01:00
parent 4db460d655
commit 63afece4a6
24 changed files with 563 additions and 297 deletions

View File

@@ -23,6 +23,7 @@ DWORD dwFrames{ 0 };
double fSimulationTime{ 0.0 };
double fSoundTimer{ 0.0 };
double fSinceStart{ 0.0 };
double override_delta = -1.0f;
double GetTime()
{
@@ -39,19 +40,9 @@ double GetDeltaRenderTime()
return DeltaRenderTime;
}
void SetDeltaTime(double t)
void set_delta_override(double t)
{
DeltaTime = t;
}
bool GetSoundTimer()
{ // Ra: być może, by dźwięki nie modyfikowały się zbyt często, po 0.1s zeruje się ten licznik
return (fSoundTimer == 0.0f);
}
double GetFPS()
{
return fFPS;
override_delta = t;
}
void ResetTimers()
@@ -59,8 +50,7 @@ void ResetTimers()
UpdateTimers( Global.iPause != 0 );
DeltaTime = 0.1;
DeltaRenderTime = 0.0;
fSoundTimer = 0.0;
};
}
uint64_t fr, count, oldCount;
@@ -89,6 +79,11 @@ void UpdateTimers(bool pause)
else
DeltaTime = 0.0; // wszystko stoi, bo czas nie płynie
if (override_delta != -1.0f)
DeltaTime = override_delta;
fSimulationTime += DeltaTime;
oldCount = count;
// Keep track of the time lapse and frame count
#if __linux__
@@ -106,7 +101,6 @@ void UpdateTimers(bool pause)
fLastTime = fTime;
dwFrames = 0L;
}
fSimulationTime += DeltaTime;
};
}; // namespace timer

View File

@@ -16,11 +16,7 @@ double GetTime();
double GetDeltaTime();
double GetDeltaRenderTime();
void SetDeltaTime(double v);
bool GetSoundTimer();
double GetFPS();
void set_delta_override(double v);
void ResetTimers();

View File

@@ -348,7 +348,12 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = {
{ user_command::generictoggle6, &TTrain::OnCommand_generictoggle },
{ user_command::generictoggle7, &TTrain::OnCommand_generictoggle },
{ user_command::generictoggle8, &TTrain::OnCommand_generictoggle },
{ user_command::generictoggle9, &TTrain::OnCommand_generictoggle }
{ user_command::generictoggle9, &TTrain::OnCommand_generictoggle },
{ user_command::vehiclemove, &TTrain::OnCommand_vehiclemove },
{ user_command::vehiclemoveforwards, &TTrain::OnCommand_vehiclemoveforwards },
{ user_command::vehiclemovebackwards, &TTrain::OnCommand_vehiclemovebackwards },
{ user_command::vehicleboost, &TTrain::OnCommand_vehicleboost },
};
std::vector<std::string> const TTrain::fPress_labels = {
@@ -4706,6 +4711,52 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com
}
}
void TTrain::OnCommand_vehiclemove(TTrain *Train, const command_data &Command) {
if (Command.action == GLFW_RELEASE || !DebugModeFlag)
return;
TDynamicObject *d = Train->DynamicObject;
while( d ) {
d->Move( Command.param1 * d->DirectionGet() );
d = d->Next(); // pozostałe też
}
d = Train->DynamicObject->Prev();
while( d ) {
d->Move( Command.param1 * d->DirectionGet() );
d = d->Prev(); // w drugą stronę też
}
}
void TTrain::OnCommand_vehiclemoveforwards(TTrain *Train, const command_data &Command) {
command_data modified = Command;
modified.param1 = 100.0;
OnCommand_vehiclemove(Train, modified);
}
void TTrain::OnCommand_vehiclemovebackwards(TTrain *Train, const command_data &Command) {
command_data modified = Command;
modified.param1 = -100.0;
OnCommand_vehiclemove(Train, modified);
}
void TTrain::OnCommand_vehicleboost(TTrain *Train, const command_data &Command) {
if (Command.action == GLFW_RELEASE || !DebugModeFlag)
return;
double boost = Command.param1 != 0.0 ? Command.param1 : 2.78;
TDynamicObject *d = Train->DynamicObject;
while( d ) {
d->MoverParameters->V += d->DirectionGet() * boost;
d = d->Next(); // pozostałe też
}
d = Train->DynamicObject->Prev();
while( d ) {
d->MoverParameters->V += d->DirectionGet() * boost;
d = d->Prev(); // w drugą stronę też
}
}
// cab movement update, fixed step part
void TTrain::UpdateCab() {
@@ -4791,9 +4842,7 @@ bool TTrain::Update( double const Deltatime )
// eventually commands are going to be retrieved directly by the vehicle, filtered through active control stand
// and ultimately executed, provided the stand allows it.
command_data commanddata;
// NOTE: currently we're only storing commands for local vehicle and there's no id system in place,
// so we're supplying 'default' vehicle id of 0
while( simulation::Commands.pop( commanddata, static_cast<std::size_t>( command_target::vehicle ) | 0 ) ) {
while( simulation::Commands.pop( commanddata, (uint32_t)command_target::vehicle | id() )) {
auto lookup = m_commandhandlers.find( commanddata.command );
if( lookup != m_commandhandlers.end() ) {
@@ -7906,3 +7955,19 @@ int TTrain::get_drive_direction()
{
return mvOccupied->ActiveDir * mvOccupied->CabNo;
}
uint16_t TTrain::id() {
static uint16_t i = 0; // todo: do something better
if (vid == 0) {
vid = ++i;
WriteLog("assigning id " + std::to_string(vid) + " to vehicle " + Dynamic()->name());
}
return vid;
}
void train_table::update(double dt)
{
for (TTrain *train : m_items) {
train->Update(dt);
}
}

13
Train.h
View File

@@ -327,6 +327,10 @@ class TTrain
static void OnCommand_cabchangeforward( TTrain *Train, command_data const &Command );
static void OnCommand_cabchangebackward( TTrain *Train, command_data const &Command );
static void OnCommand_generictoggle( TTrain *Train, command_data const &Command );
static void OnCommand_vehiclemove( TTrain *Train, command_data const &Command );
static void OnCommand_vehiclemoveforwards( TTrain *Train, command_data const &Command );
static void OnCommand_vehiclemovebackwards( TTrain *Train, command_data const &Command );
static void OnCommand_vehicleboost( TTrain *Train, command_data const &Command );
// members
@@ -642,6 +646,7 @@ private:
float fPPress, fNPress;
int iRadioChannel { 1 }; // numer aktualnego kana?u radiowego
std::vector<std::tuple<std::string, texture_handle, std::optional<texture_window>>> m_screens;
uint16_t vid = 0;
public:
float fPress[20][3]; // cisnienia dla wszystkich czlonow
@@ -674,5 +679,11 @@ private:
void set_scndctrl(int);
void set_trainbrake(float);
void set_localbrake(float);
uint16_t id();
};
class train_table : public basic_table<TTrain> {
public:
void update(double dt);
};
//---------------------------------------------------------------------------

View File

@@ -153,8 +153,35 @@ eu07_application::run() {
Timer::subsystem.mainloop_total.start();
glfwPollEvents();
if (!m_modes[ m_modestack.top() ]->update())
break;
if (!m_network)
{
if (!m_modes[ m_modestack.top() ]->update())
break;
}
else if (!Global.network_conf.is_server) {
double delta;
do {
auto tup = m_network->get_next_delta();
delta = std::get<0>(tup);
simulation::Commands.merge_command_sequences(std::get<1>(tup));
Timer::set_delta_override(delta);
simulation::Commands.update();
if (!m_modes[ m_modestack.top() ]->update())
break;
}
while (delta != 0.0);
}
else {
auto commands = simulation::Commands.peek_command_sequences();
simulation::Commands.update();
if (!m_modes[ m_modestack.top() ]->update())
break;
double delta = Timer::GetDeltaTime();
m_network->push_delta(delta, commands);
}
m_taskqueue.update();
opengl_texture::reset_unit_cache();
@@ -169,7 +196,6 @@ eu07_application::run() {
m_modes[ m_modestack.top() ]->on_event_poll();
simulation::Commands.update();
if (m_screenshot_queued) {
m_screenshot_queued = false;
screenshot_man.make_screenshot();
@@ -177,7 +203,7 @@ eu07_application::run() {
Timer::subsystem.mainloop_total.stop();
if (Global.network_conf.enabled)
if (m_network)
m_network->poll();
}

View File

@@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/.
#include "Logs.h"
#include "Timer.h"
#include "utilities.h"
#include "simulation.h"
namespace simulation {
@@ -217,7 +218,14 @@ commanddescription_sequence Commands_descriptions = {
{ "batterydisable", command_target::vehicle, command_mode::oneoff },
{ "motorblowerstogglefront", command_target::vehicle, command_mode::oneoff },
{ "motorblowerstogglerear", command_target::vehicle, command_mode::oneoff },
{ "motorblowersdisableall", command_target::vehicle, command_mode::oneoff }
{ "motorblowersdisableall", command_target::vehicle, command_mode::oneoff },
{ "timejump", command_target::simulation, command_mode::oneoff },
{ "timejumplarge", command_target::simulation, command_mode::oneoff },
{ "timejumpsmall", command_target::simulation, command_mode::oneoff },
{ "vehiclemove", command_target::vehicle, command_mode::oneoff },
{ "vehiclemoveforwards", command_target::vehicle, command_mode::oneoff },
{ "vehiclemovebackwards", command_target::vehicle, command_mode::oneoff },
{ "vehicleboost", command_target::vehicle, command_mode::oneoff },
};
} // simulation
@@ -225,12 +233,12 @@ commanddescription_sequence Commands_descriptions = {
void command_queue::update()
{
double delta = Timer::GetDeltaTime();
for (user_command c : m_active_continuous)
for (auto c : m_active_continuous)
{
auto const &desc = simulation::Commands_descriptions[ static_cast<std::size_t>( c ) ];
command_data data { c, GLFW_REPEAT, 0, 0, delta };
auto lookup = m_commands.emplace((size_t)desc.target, commanddata_sequence() );
lookup.first->second.emplace( data );
auto lookup = m_commands.emplace(c.second, commanddata_sequence() );
command_data data({c.first, GLFW_REPEAT, 0.0, 0.0, delta});
lookup.first->second.emplace_back(data);
}
}
@@ -241,16 +249,16 @@ command_queue::push( command_data const &Command, std::size_t const Recipient )
if (desc.mode == command_mode::continuous)
{
if (Command.action == GLFW_PRESS)
m_active_continuous.emplace(Command.command);
m_active_continuous.emplace(std::make_pair(Command.command, Recipient));
else if (Command.action == GLFW_RELEASE)
m_active_continuous.erase(Command.command);
m_active_continuous.erase(std::make_pair(Command.command, Recipient));
else if (Command.action == GLFW_REPEAT)
return;
}
auto lookup = m_commands.emplace( Recipient, commanddata_sequence() );
// recipient stack was either located or created, so we can add to it quite safely
lookup.first->second.emplace( Command );
lookup.first->second.emplace_back( Command );
}
// retrieves oldest posted command for specified recipient, if any. returns: true on retrieval, false if there's nothing to retrieve
@@ -269,16 +277,24 @@ command_queue::pop( command_data &Command, std::size_t const Recipient ) {
}
// we have command stack with command(s) on it, retrieve and pop the first one
Command = commands.front();
commands.pop();
commands.pop_front();
return true;
}
void
command_relay::post( user_command const Command, double const Param1, double const Param2,
int const Action, std::uint16_t const Recipient) const {
int const Action, uint16_t Recipient) const {
auto const &command = simulation::Commands_descriptions[ static_cast<std::size_t>( Command ) ];
if (command.target == command_target::vehicle && Recipient == 0) {
// default 0 recipient is currently controlled train
if (simulation::Train == nullptr)
return;
Recipient = simulation::Train->id();
}
if( ( command.target == command_target::vehicle )
&& ( true == FreeFlyModeFlag )
&& ( ( false == DebugModeFlag )

View File

@@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/.
enum class user_command {
aidriverenable,
aidriverenable = 0,
aidriverdisable,
mastercontrollerincrease,
mastercontrollerincreasefast,
@@ -213,13 +213,18 @@ enum class user_command {
motorblowerstogglerear,
motorblowersdisableall,
timejump,
timejumplarge,
timejumpsmall,
vehiclemove,
vehiclemoveforwards,
vehiclemovebackwards,
vehicleboost,
none = -1
};
enum class command_target {
userinterface,
simulation,
/*
// NOTE: there's no need for consist- and unit-specific commands at this point, but it's a possibility.
// since command targets are mutually exclusive these don't reduce ranges for individual vehicles etc
@@ -229,7 +234,9 @@ enum class command_target {
// values are combined with object id. 0xffff objects of each type should be quite enough ("for everyone")
vehicle = 0x10000,
signal = 0x20000,
entity = 0x40000
entity = 0x40000,
simulation = 0x8000,
};
enum class command_mode {
@@ -259,6 +266,10 @@ struct command_data {
class command_queue {
public:
// types
typedef std::deque<command_data> commanddata_sequence;
typedef std::unordered_map<std::size_t, commanddata_sequence> commanddatasequence_map;
// methods
// posts specified command for specified recipient
void
@@ -268,16 +279,33 @@ public:
pop( command_data &Command, std::size_t const Recipient );
void update();
commanddatasequence_map peek_command_sequences() const {
commanddatasequence_map map(m_commands);
for (auto &kv : map) {
if (kv.first & 0)
kv.second.clear();
}
return map;
}
void merge_command_sequences(commanddatasequence_map map) {
for (auto const &kv : map)
for (command_data const &data : kv.second)
push(data, kv.first);
}
private:
// types
typedef std::queue<command_data> commanddata_sequence;
typedef std::unordered_map<std::size_t, commanddata_sequence> commanddatasequence_map;
// members
commanddatasequence_map m_commands;
// TODO: this set should contain more than just user_command
// also, maybe that and all continuous input logic should be in command_relay?
std::unordered_set<user_command> m_active_continuous;
struct command_set_hash {
uint64_t operator() (const std::pair<user_command, uint32_t> &pair) const {
return ((uint64_t)pair.first << 32) | ((uint64_t) pair.second);
}
};
std::unordered_set<std::pair<user_command, uint32_t>, command_set_hash> m_active_continuous;
};
// NOTE: simulation should be a (light) wrapper rather than namespace so we could potentially instance it,
@@ -299,11 +327,10 @@ class command_relay {
public:
// constructors
// methods
// posts specified command for the specified recipient
// TODO: replace uint16_t with recipient handle, based on item id
// posts specified command for the specified recipient
void
post( user_command const Command, double const Param1, double const Param2,
int const Action, std::uint16_t const Recipient ) const;
post(user_command const Command, double const Param1, double const Param2,
int const Action, uint16_t Recipient ) const;
private:
// types
// members

View File

@@ -219,8 +219,14 @@ driverkeyboard_input::default_bindings() {
// batterydisable,
{ user_command::motorblowerstogglefront, GLFW_KEY_N | keymodifier::shift },
{ user_command::motorblowerstogglerear, GLFW_KEY_M | keymodifier::shift },
{ user_command::motorblowersdisableall, GLFW_KEY_M | keymodifier::control }
{ user_command::motorblowersdisableall, GLFW_KEY_M | keymodifier::control },
// admin_timejump,
{ user_command::timejumplarge, GLFW_KEY_F1 | keymodifier::control },
{ user_command::timejumpsmall, GLFW_KEY_F1 | keymodifier::shift },
// admin_vehiclemove,
{ user_command::vehiclemoveforwards, GLFW_KEY_LEFT_BRACKET | keymodifier::control },
{ user_command::vehiclemovebackwards, GLFW_KEY_RIGHT_BRACKET | keymodifier::control },
{ user_command::vehicleboost, GLFW_KEY_TAB | keymodifier::control },
};
}

View File

@@ -100,106 +100,106 @@ driver_mode::update() {
double const deltatime = Timer::GetDeltaTime(); // 0.0 gdy pauza
if( Global.iPause == 0 ) {
simulation::State.update_clocks();
simulation::Environment.update();
if (deltatime != 0.0)
{
// jak pauza, to nie ma po co tego przeliczać
simulation::Time.update( deltatime );
}
simulation::State.update_clocks();
simulation::Environment.update();
// fixed step, simulation time based updates
// m_primaryupdateaccumulator += dt; // unused for the time being
m_secondaryupdateaccumulator += deltatime;
/*
// NOTE: until we have no physics state interpolation during render, we need to rely on the old code,
// as doing fixed step calculations but flexible step render results in ugly mini jitter
// core routines (physics)
int updatecount = 0;
while( ( m_primaryupdateaccumulator >= m_primaryupdaterate )
&&( updatecount < 20 ) ) {
// no more than 20 updates per single pass, to keep physics from hogging up all run time
Ground.Update( m_primaryupdaterate, 1 );
++updatecount;
m_primaryupdateaccumulator -= m_primaryupdaterate;
}
*/
int updatecount = 1;
if( deltatime > m_primaryupdaterate ) // normalnie 0.01s
{
/*
// NOTE: experimentally disabled physics update cap
auto const iterations = std::ceil(dt / m_primaryupdaterate);
updatecount = std::min( 20, static_cast<int>( iterations ) );
*/
updatecount = std::ceil( deltatime / m_primaryupdaterate );
/*
// NOTE: changing dt wrecks things further down the code. re-acquire proper value later or cleanup here
dt = dt / iterations; // Ra: fizykę lepiej by było przeliczać ze stałym krokiem
*/
}
auto const stepdeltatime { deltatime / updatecount };
// NOTE: updates are limited to 20, but dt is distributed over potentially many more iterations
// this means at count > 20 simulation and render are going to desync. is that right?
// NOTE: experimentally changing this to prevent the desync.
// TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps
Timer::subsystem.sim_dynamics.start();
if( true == Global.FullPhysics ) {
// mixed calculation mode, steps calculated in ~0.05s chunks
while( updatecount >= 5 ) {
simulation::State.update( stepdeltatime, 5 );
updatecount -= 5;
}
if( updatecount ) {
simulation::State.update( stepdeltatime, updatecount );
}
}
else {
// simplified calculation mode; faster but can lead to errors
simulation::State.update( stepdeltatime, updatecount );
}
Timer::subsystem.sim_dynamics.stop();
// m_primaryupdateaccumulator += dt; // unused for the time being
m_secondaryupdateaccumulator += deltatime;
/*
// NOTE: until we have no physics state interpolation during render, we need to rely on the old code,
// as doing fixed step calculations but flexible step render results in ugly mini jitter
// core routines (physics)
int updatecount = 0;
while( ( m_primaryupdateaccumulator >= m_primaryupdaterate )
&&( updatecount < 20 ) ) {
// no more than 20 updates per single pass, to keep physics from hogging up all run time
Ground.Update( m_primaryupdaterate, 1 );
++updatecount;
m_primaryupdateaccumulator -= m_primaryupdaterate;
}
*/
int updatecount = 1;
if( deltatime > m_primaryupdaterate ) // normalnie 0.01s
{
/*
// NOTE: experimentally disabled physics update cap
auto const iterations = std::ceil(dt / m_primaryupdaterate);
updatecount = std::min( 20, static_cast<int>( iterations ) );
*/
updatecount = std::ceil( deltatime / m_primaryupdaterate );
/*
// NOTE: changing dt wrecks things further down the code. re-acquire proper value later or cleanup here
dt = dt / iterations; // Ra: fizykę lepiej by było przeliczać ze stałym krokiem
*/
}
auto const stepdeltatime { deltatime / updatecount };
// NOTE: updates are limited to 20, but dt is distributed over potentially many more iterations
// this means at count > 20 simulation and render are going to desync. is that right?
// NOTE: experimentally changing this to prevent the desync.
// TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps
Timer::subsystem.sim_dynamics.start();
if( true == Global.FullPhysics ) {
// mixed calculation mode, steps calculated in ~0.05s chunks
while( updatecount >= 5 ) {
simulation::State.update( stepdeltatime, 5 );
updatecount -= 5;
}
if( updatecount ) {
simulation::State.update( stepdeltatime, updatecount );
}
}
else {
// simplified calculation mode; faster but can lead to errors
simulation::State.update( stepdeltatime, updatecount );
}
Timer::subsystem.sim_dynamics.stop();
// secondary fixed step simulation time routines
while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) {
// secondary fixed step simulation time routines
while( m_secondaryupdateaccumulator >= m_secondaryupdaterate ) {
// awaria PoKeys mogła włączyć pauzę - przekazać informację
if( Global.iMultiplayer ) // dajemy znać do serwera o wykonaniu
if( iPause != Global.iPause ) { // przesłanie informacji o pauzie do programu nadzorującego
multiplayer::WyslijParam( 5, 3 ); // ramka 5 z czasem i stanem zapauzowania
iPause = Global.iPause;
}
// awaria PoKeys mogła włączyć pauzę - przekazać informację
if( Global.iMultiplayer ) // dajemy znać do serwera o wykonaniu
if( iPause != Global.iPause ) { // przesłanie informacji o pauzie do programu nadzorującego
multiplayer::WyslijParam( 5, 3 ); // ramka 5 z czasem i stanem zapauzowania
iPause = Global.iPause;
}
// TODO: generic shake update pass for vehicles within view range
if( Camera.m_owner != nullptr ) {
Camera.m_owner->update_shake( m_secondaryupdaterate );
}
// TODO: generic shake update pass for vehicles within view range
if( Camera.m_owner != nullptr ) {
Camera.m_owner->update_shake( m_secondaryupdaterate );
}
m_secondaryupdateaccumulator -= m_secondaryupdaterate; // these should be inexpensive enough we have no cap
}
m_secondaryupdateaccumulator -= m_secondaryupdaterate; // these should be inexpensive enough we have no cap
}
// variable step simulation time routines
// variable step simulation time routines
if( ( simulation::Train == nullptr ) && ( false == FreeFlyModeFlag ) ) {
// intercept cases when the driven train got removed after entering portal
InOutKey();
}
if( ( simulation::Train == nullptr ) && ( false == FreeFlyModeFlag ) ) {
// intercept cases when the driven train got removed after entering portal
InOutKey();
}
if( Global.changeDynObj ) {
// ABu zmiana pojazdu - przejście do innego
ChangeDynamic();
}
if( Global.changeDynObj ) {
// ABu zmiana pojazdu - przejście do innego
ChangeDynamic();
}
if( simulation::Train != nullptr ) {
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( simulation::Train->Dynamic() );
simulation::Train->Update( deltatime );
}
else {
TSubModel::iInstance = 0;
}
if( simulation::Train != nullptr )
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( simulation::Train->Dynamic() );
else
TSubModel::iInstance = 0;
simulation::Events.update();
simulation::Region->update_events();
simulation::Lights.update();
simulation::Trains.update(deltatime);
simulation::Events.update();
simulation::Region->update_events();
simulation::Lights.update();
}
// render time routines follow:
@@ -263,24 +263,22 @@ driver_mode::enter() {
{
WriteLog( "Initializing player train, \"" + Global.asHumanCtrlVehicle + "\"" );
if( simulation::Train == nullptr ) {
simulation::Train = new TTrain();
}
if( simulation::Train->Init( nPlayerTrain ) )
{
WriteLog("Player train initialization OK");
TTrain *train = new TTrain();
if (train->Init(nPlayerTrain)) {
simulation::Trains.insert(train, nPlayerTrain->name());
simulation::Train = train;
Application.set_title( Global.AppName + " (" + simulation::Train->Controlled()->Name + " @ " + Global.SceneryFile + ")" );
WriteLog("Player train initialization OK");
Application.set_title( Global.AppName + " (" + simulation::Train->Controlled()->Name + " @ " + Global.SceneryFile + ")" );
CabView();
}
else {
delete train;
CabView();
}
else
{
Error("Bad init: player train initialization failed");
FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie
SafeDelete( simulation::Train );
Camera.m_owner = nullptr;
}
Error("Bad init: player train initialization failed");
FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie
Camera.m_owner = nullptr;
}
}
else
{
@@ -672,22 +670,6 @@ driver_mode::OnKeyDown(int cKey) {
switch (cKey) {
case GLFW_KEY_F1: {
if( DebugModeFlag ) {
// additional simulation clock jump keys in debug mode
if( Global.ctrlState ) {
// ctrl-f1
simulation::Time.update( 20.0 * 60.0 );
}
else if( Global.shiftState ) {
// shift-f1
simulation::Time.update( 5.0 * 60.0 );
}
}
break;
}
case GLFW_KEY_F4: {
if( Global.shiftState ) { ExternalView(); } // with Shift, cycle through external views
@@ -713,19 +695,22 @@ driver_mode::OnKeyDown(int cKey) {
if( ( true == DebugModeFlag )
|| ( tmp->MoverParameters->Vel <= 5.0 ) ) {
// works always in debug mode, or for stopped/slow moving vehicles otherwise
if( simulation::Train == nullptr ) {
simulation::Train = new TTrain(); // jeśli niczym jeszcze nie jeździlismy
}
if( simulation::Train->Init( tmp ) ) { // przejmujemy sterowanie
simulation::Train->Dynamic()->Mechanik->TakeControl( false );
}
else {
SafeDelete( simulation::Train ); // i nie ma czym sterować
}
if( simulation::Train ) {
InOutKey(); // do kabiny
}
TTrain *train = simulation::Trains.find(tmp->name());
if (train == nullptr) {
train = new TTrain();
if (train->Init(tmp)) {
simulation::Trains.insert(train, tmp->name());
}
else {
delete train;
train = nullptr;
}
}
if (train != nullptr) {
train->Dynamic()->Mechanik->TakeControl( false );
simulation::Train = train;
InOutKey();
}
}
}
break;
@@ -780,58 +765,6 @@ driver_mode::OnKeyDown(int cKey) {
break;
}
case GLFW_KEY_LEFT_BRACKET:
case GLFW_KEY_RIGHT_BRACKET:
case GLFW_KEY_TAB: {
// consist movement in debug mode
if( ( true == DebugModeFlag )
&& ( false == Global.shiftState )
&& ( true == Global.ctrlState )
&& ( simulation::Train != nullptr )
&& ( simulation::Train->Dynamic()->Controller == Humandriver ) ) {
if( DebugModeFlag ) {
// przesuwanie składu o 100m
auto *vehicle { simulation::Train->Dynamic() };
TDynamicObject *d = vehicle;
if( cKey == GLFW_KEY_LEFT_BRACKET ) {
while( d ) {
d->Move( 100.0 * d->DirectionGet() );
d = d->Next(); // pozostałe też
}
d = vehicle->Prev();
while( d ) {
d->Move( 100.0 * d->DirectionGet() );
d = d->Prev(); // w drugą stronę też
}
}
else if( cKey == GLFW_KEY_RIGHT_BRACKET ) {
while( d ) {
d->Move( -100.0 * d->DirectionGet() );
d = d->Next(); // pozostałe też
}
d = vehicle->Prev();
while( d ) {
d->Move( -100.0 * d->DirectionGet() );
d = d->Prev(); // w drugą stronę też
}
}
else if( cKey == GLFW_KEY_TAB ) {
while( d ) {
d->MoverParameters->V += d->DirectionGet()*2.78;
d = d->Next(); // pozostałe też
}
d = vehicle->Prev();
while( d ) {
d->MoverParameters->V += d->DirectionGet()*2.78;
d = d->Prev(); // w drugą stronę też
}
}
}
}
break;
}
default: {
break;
}

View File

@@ -210,9 +210,7 @@ drivermouse_input::move( double Mousex, double Mousey ) {
Mousex,
Mousey,
GLFW_PRESS,
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
else {
// control picking mode
@@ -223,8 +221,7 @@ drivermouse_input::move( double Mousex, double Mousey ) {
m_slider.value(),
0,
GLFW_PRESS,
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
if( false == m_pickmodepanning ) {
@@ -240,9 +237,7 @@ drivermouse_input::move( double Mousex, double Mousey ) {
viewoffset.x,
viewoffset.y,
GLFW_PRESS,
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
m_cursorposition = cursorposition;
}
}
@@ -315,7 +310,7 @@ drivermouse_input::button( int const Button, int const Action ) {
// NOTE: basic keyboard controls don't have any parameters
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( mousecommand, 0, 0, Action, 0 );
m_relay.post( mousecommand, 0, 0, Action, 0 );
mousecommand = user_command::none;
}
else {
@@ -393,9 +388,9 @@ drivermouse_input::button( int const Button, int const Action ) {
// NOTE: basic keyboard controls don't have any parameters
// NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( mousecommand, 0, 0, Action, 0 );
m_relay.post( mousecommand, 0, 0, Action, 0 );
if (!pickwaiting) // already depressed
m_relay.post( mousecommand, 0, 0, GLFW_RELEASE, 0 );
m_relay.post( mousecommand, 0, 0, GLFW_RELEASE, 0 );
m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one
}
else {
@@ -431,13 +426,13 @@ drivermouse_input::poll() {
// NOTE: basic keyboard controls don't have any parameters
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( m_mousecommandleft, 0, 0, GLFW_REPEAT, 0 );
m_relay.post( m_mousecommandleft, 0, 0, GLFW_REPEAT, 0 );
}
if( m_mousecommandright != user_command::none ) {
// NOTE: basic keyboard controls don't have any parameters
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( m_mousecommandright, 0, 0, GLFW_REPEAT, 0 );
m_relay.post( m_mousecommandright, 0, 0, GLFW_REPEAT, 0 );
}
m_updateaccumulator -= updaterate;
}

View File

@@ -32,9 +32,8 @@ editormouse_input::position( double Horizontal, double Vertical ) {
viewoffset.x,
viewoffset.y,
GLFW_PRESS,
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
m_cursorposition = cursorposition;
}

View File

@@ -201,9 +201,7 @@ gamepad_input::process_axes( glm::vec2 Leftstick, glm::vec2 const &Rightstick, g
turnx,
turny,
GLFW_PRESS,
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
// left stick, either movement or controls, depending on currently active mode
@@ -216,7 +214,7 @@ gamepad_input::process_axes( glm::vec2 Leftstick, glm::vec2 const &Rightstick, g
Leftstick.x,
Leftstick.y,
GLFW_PRESS,
0 );
0 );
}
}
else {

View File

@@ -59,7 +59,11 @@ keyboard_input::recall_bindings() {
{ "num_7", GLFW_KEY_KP_7 }, { "num_8", GLFW_KEY_KP_8 }, { "num_9", GLFW_KEY_KP_9 }, { "num_+", GLFW_KEY_KP_ADD },
{ "num_4", GLFW_KEY_KP_4 }, { "num_5", GLFW_KEY_KP_5 }, { "num_6", GLFW_KEY_KP_6 },
{ "num_1", GLFW_KEY_KP_1 }, { "num_2", GLFW_KEY_KP_2 }, { "num_3", GLFW_KEY_KP_3 }, { "num_enter", GLFW_KEY_KP_ENTER },
{ "num_0", GLFW_KEY_KP_0 }, { "num_.", GLFW_KEY_KP_DECIMAL }
{ "num_0", GLFW_KEY_KP_0 }, { "num_.", GLFW_KEY_KP_DECIMAL },
// misc
{ "f1", GLFW_KEY_F1 }, { "f2", GLFW_KEY_F2 }, { "f3", GLFW_KEY_F3 }, { "f4", GLFW_KEY_F4 }, { "f5", GLFW_KEY_F5 },
{ "f6", GLFW_KEY_F6 }, { "f7", GLFW_KEY_F7 }, { "f8", GLFW_KEY_F8 }, { "f9", GLFW_KEY_F9 }, { "f10", GLFW_KEY_F10 },
{ "f11", GLFW_KEY_F11 }, { "f12", GLFW_KEY_F12 }, { "tab", GLFW_KEY_TAB }
};
// NOTE: to simplify things we expect one entry per line, and whole entry in one line
@@ -167,7 +171,7 @@ keyboard_input::key( int const Key, int const Action ) {
// NOTE: basic keyboard controls don't have any parameters
// as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0
// TODO: pass correct entity id once the missing systems are in place
m_relay.post( lookup->second, 0, 0, Action, 0 );
m_relay.post( lookup->second, 0, 0, Action, 0 );
m_command = (
Action == GLFW_RELEASE ?
user_command::none :
@@ -247,7 +251,7 @@ keyboard_input::poll() {
movementhorizontal.x,
movementhorizontal.y,
GLFW_PRESS,
0 );
0 );
}
m_movementhorizontal = movementhorizontal;
@@ -268,7 +272,7 @@ keyboard_input::poll() {
movementvertical,
0,
GLFW_PRESS,
0 );
0 );
}
m_movementvertical = movementvertical;

View File

@@ -7,7 +7,7 @@ network::manager::manager()
void network::manager::create_server()
{
servers.emplace_back(std::make_shared<tcp_server>(io_context));
server = std::make_shared<tcp_server>(io_context);
}
void network::manager::poll()
@@ -19,3 +19,13 @@ void network::manager::connect()
{
client = std::make_shared<tcp_client>(io_context);
}
std::tuple<double, command_queue::commanddatasequence_map> network::manager::get_next_delta()
{
return client->get_next_delta();
}
void network::manager::push_delta(double delta, command_queue::commanddatasequence_map commands)
{
server->push_delta(delta, commands);
}

View File

@@ -2,13 +2,14 @@
#include <asio.hpp>
#include <memory>
#include "network/network.h"
#include "command.h"
namespace network
{
class manager
{
asio::io_context io_context;
std::vector<std::shared_ptr<server>> servers;
std::shared_ptr<network::server> server;
std::shared_ptr<network::client> client;
public:
@@ -17,5 +18,8 @@ namespace network
void create_server();
void connect();
void poll();
std::tuple<double, command_queue::commanddatasequence_map> get_next_delta();
void push_delta(double delta, command_queue::commanddatasequence_map commands);
};
}

View File

@@ -3,16 +3,85 @@
void network::message::serialize(std::ostream &stream)
{
sn_utils::ls_uint16(stream, (uint16_t)type);
}
network::message network::message::deserialize(std::istream &stream)
void network::message::deserialize(std::istream &stream)
{
type_e type = (type_e)sn_utils::ld_uint16(stream);
if ((int)type > (int)message::TYPE_MAX)
{
type = message::TYPE_MAX;
}
void::network::delta_message::serialize(std::ostream &stream)
{
sn_utils::ls_float64(stream, dt);
sn_utils::ls_uint32(stream, commands.size());
for (auto const &kv : commands) {
sn_utils::ls_uint32(stream, kv.first);
sn_utils::ls_uint32(stream, kv.second.size());
for (command_data const &data : kv.second) {
sn_utils::ls_uint32(stream, (uint32_t)data.command);
sn_utils::ls_int32(stream, data.action);
sn_utils::ls_float64(stream, data.param1);
sn_utils::ls_float64(stream, data.param2);
sn_utils::ls_float64(stream, data.time_delta);
}
}
}
void network::delta_message::deserialize(std::istream &stream)
{
dt = sn_utils::ld_float64(stream);
uint32_t commands_size = sn_utils::ld_uint32(stream);
for (uint32_t i = 0; i < commands_size; i++) {
uint32_t recipient = sn_utils::ld_uint32(stream);
uint32_t sequence_size = sn_utils::ld_uint32(stream);
command_queue::commanddata_sequence sequence;
for (uint32_t i = 0; i < sequence_size; i++) {
command_data data;
data.command = (user_command)sn_utils::ld_uint32(stream);
data.action = sn_utils::ld_int32(stream);
data.param1 = sn_utils::ld_float64(stream);
data.param2 = sn_utils::ld_float64(stream);
data.time_delta = sn_utils::ld_float64(stream);
sequence.emplace_back(data);
}
commands.emplace(recipient, sequence);
}
}
size_t network::delta_message::get_size()
{
size_t cmd_size = 4;
for (auto const &kv : commands) {
cmd_size += 8;
for (command_data const &data : kv.second) {
cmd_size += 8 + 3 * 8;
}
}
return message({type});
return message::get_size() + cmd_size + 8;
}
std::shared_ptr<network::message> network::deserialize_message(std::istream &stream)
{
message::type_e type = (message::type_e)sn_utils::ld_uint16(stream);
std::shared_ptr<message> msg;
if (type == message::STEP_INFO) {
auto m = std::make_shared<delta_message>();
m->type = type;
m->deserialize(stream);
msg = m;
}
else {
msg = std::make_shared<message>(type);
}
return msg;
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include "network/message.h"
#include "command.h"
#include <queue>
namespace network
{
@@ -16,8 +18,23 @@ namespace network
type_e type;
void serialize(std::ostream &stream);
static message deserialize(std::istream &stream);
message(type_e t) : type(t) {}
virtual void serialize(std::ostream &stream);
virtual void deserialize(std::istream &stream);
virtual size_t get_size() { return 2; }
};
struct delta_message : public message
{
delta_message() : message(STEP_INFO) {}
double dt;
command_queue::commanddatasequence_map commands;
virtual void serialize(std::ostream &stream) override;
virtual void deserialize(std::istream &stream) override;
virtual size_t get_size() override;
};
std::shared_ptr<message> deserialize_message(std::istream &stream);
}

View File

@@ -2,52 +2,102 @@
#include "network/message.h"
#include "Logs.h"
#include "sn_utils.h"
#include "Timer.h"
void network::connection::connected()
{
WriteLog("net: socket connected", logtype::net);
message hello;
hello.type = message::CONNECT_REQUEST;
std::shared_ptr<message> hello = std::make_shared<message>(message::CONNECT_REQUEST);
send_message(hello);
}
void network::connection::message_received(message &msg)
void network::connection::message_received(std::shared_ptr<message> &msg)
{
if (msg.type == message::TYPE_MAX)
if (msg->type == message::TYPE_MAX)
{
disconnect();
return;
}
if (msg.type == message::CONNECT_REQUEST)
if (msg->type == message::CONNECT_REQUEST)
{
message reply({message::CONNECT_ACCEPT});
std::shared_ptr<message> reply = std::make_shared<message>(message::CONNECT_ACCEPT);
WriteLog("client accepted", logtype::net);
std::string reply_buf;
std::ostringstream stream(reply_buf);
reply.serialize(stream);
send_message(reply);
}
else if (msg->type == message::CONNECT_ACCEPT)
{
WriteLog("accept received", logtype::net);
}
else if (msg->type == message::STEP_INFO)
{
auto delta = std::dynamic_pointer_cast<delta_message>(msg);
auto now = std::chrono::high_resolution_clock::now();
delta_queue.push(std::make_pair(now, delta));
}
}
std::tuple<double, command_queue::commanddatasequence_map> network::connection::get_next_delta()
{
if (delta_queue.empty()) {
return std::tuple<double,
command_queue::commanddatasequence_map>(0.0, command_queue::commanddatasequence_map());
}
///auto now = std::chrono::high_resolution_clock::now();
//double last_frame = std::chrono::duration_cast<std::chrono::seconds>(now - last_time).count();
//last_time = now;
//accum += last_frame;
auto entry = delta_queue.front();
//if (accum > remote_dt) {
//accum -= remote_dt;
delta_queue.pop();
return std::make_tuple(entry.second->dt, entry.second->commands);
//}
//else {
//return 0.0;
//}
}
void network::connection::data_received(std::string &buffer)
{
std::istringstream body(buffer);
message msg = message::deserialize(body);
std::shared_ptr<message> msg = deserialize_message(body);
message_received(msg);
}
void network::connection::send_message(message &msg)
void network::connection::send_message(std::shared_ptr<message> msg)
{
std::ostringstream stream;
sn_utils::ls_uint32(stream, 0x37305545);
sn_utils::ls_uint32(stream, 2);
msg.serialize(stream);
sn_utils::ls_uint32(stream, msg->get_size());
sn_utils::ls_uint16(stream, (uint16_t)msg->type);
msg->serialize(stream);
stream.flush();
std::shared_ptr<std::string> buf = std::make_shared<std::string>(stream.str());
send_data(buf);
}
void network::server::push_delta(double dt, command_queue::commanddatasequence_map commands)
{
std::shared_ptr<delta_message> msg = std::make_shared<delta_message>();
msg->dt = dt;
msg->commands = commands;
for (auto c : clients)
c->send_message(msg);
}
std::tuple<double, command_queue::commanddatasequence_map> network::client::get_next_delta()
{
return conn->get_next_delta();
}

View File

@@ -3,15 +3,23 @@
#include <memory>
#include <functional>
#include <optional>
#include <queue>
#include <chrono>
#include "network/message.h"
#include "command.h"
namespace network
{
class connection : public std::enable_shared_from_this<connection>
{
private:
void message_received(message &msg);
void send_message(message &msg);
void message_received(std::shared_ptr<message> &msg);
std::queue<
std::pair<std::chrono::high_resolution_clock::time_point,
std::shared_ptr<delta_message>>> delta_queue;
//std::chrono::high_resolution_clock::time_point last_time;
//double accum = -1.0;
protected:
virtual void disconnect() = 0;
@@ -20,16 +28,27 @@ namespace network
void data_received(std::string &buffer);
public:
void send_message(std::shared_ptr<message> msg);
virtual void connected();
std::tuple<double, command_queue::commanddatasequence_map> get_next_delta();
};
class server
{
protected:
std::vector<std::shared_ptr<connection>> clients;
public:
void push_delta(double dt, command_queue::commanddatasequence_map commands);
};
class client
{
protected:
std::shared_ptr<connection> conn;
public:
std::tuple<double, command_queue::commanddatasequence_map> get_next_delta();
};
}

View File

@@ -117,10 +117,11 @@ void network::tcp_server::handle_accept(std::shared_ptr<tcp_conn> conn, const as
network::tcp_client::tcp_client(asio::io_context &io_ctx)
{
conn = std::make_shared<tcp_conn>(io_ctx);
auto tcpconn = std::static_pointer_cast<tcp_conn>(conn);
asio::ip::tcp::endpoint endpoint(
asio::ip::address::from_string("127.0.0.1"), 7424);
conn->socket().async_connect(endpoint,
tcpconn->socket().async_connect(endpoint,
std::bind(&tcp_client::handle_accept, this, std::placeholders::_1));
}

View File

@@ -47,7 +47,6 @@ namespace network
class tcp_client : public client
{
private:
std::shared_ptr<tcp_conn> conn;
void handle_accept(const asio::error_code &err);
public:

View File

@@ -34,6 +34,7 @@ traction_table Traction;
powergridsource_table Powergrid;
instance_table Instances;
vehicle_table Vehicles;
train_table Trains;
light_array Lights;
sound_table Sounds;
lua Lua;
@@ -64,6 +65,8 @@ state_manager::update( double const Deltatime, int Iterationcount ) {
return;
}
process_commands();
auto const totaltime { Deltatime * Iterationcount };
// NOTE: we perform animations first, as they can determine factors like contact with powergrid
TAnimModel::AnimUpdate( totaltime ); // wykonanie zakolejkowanych animacji
@@ -72,6 +75,26 @@ state_manager::update( double const Deltatime, int Iterationcount ) {
simulation::Vehicles.update( Deltatime, Iterationcount );
}
void state_manager::process_commands() {
command_data commanddata;
while( Commands.pop( commanddata, (uint32_t)command_target::simulation )) {
if (commanddata.action == GLFW_RELEASE)
continue;
if (DebugModeFlag) {
if (commanddata.command == user_command::timejump) {
Time.update(commanddata.param1);
}
else if (commanddata.command == user_command::timejumplarge) {
Time.update(20.0 * 60.0);
}
else if (commanddata.command == user_command::timejumpsmall) {
Time.update(5.0 * 60.0);
}
}
}
}
void
state_manager::update_clocks() {

View File

@@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h"
#include "lua.h"
#include "Event.h"
#include "Train.h"
namespace simulation {
@@ -35,6 +36,8 @@ public:
private:
// members
state_serializer m_serializer;
void process_commands();
};
// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel
@@ -48,6 +51,7 @@ extern traction_table Traction;
extern powergridsource_table Powergrid;
extern instance_table Instances;
extern vehicle_table Vehicles;
extern train_table Trains;
extern light_array Lights;
extern sound_table Sounds;
extern lua Lua;

View File

@@ -180,7 +180,7 @@ void uart_input::poll()
std::get<3>( entry ) ) ) };
// TODO: pass correct entity id once the missing systems are in place
relay.post( command, 0, 0, action, 0 );
relay.post( command, 0, 0, action, 0 );
}
if( true == conf.mainenable ) {
@@ -191,7 +191,7 @@ void uart_input::poll()
0,
GLFW_PRESS,
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
if( true == conf.scndenable ) {
// second controller
@@ -201,7 +201,7 @@ void uart_input::poll()
0,
GLFW_PRESS,
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
if( true == conf.trainenable ) {
// train brake
@@ -212,7 +212,7 @@ void uart_input::poll()
0,
GLFW_PRESS,
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
if( true == conf.localenable ) {
// independent brake
@@ -223,7 +223,7 @@ void uart_input::poll()
0,
GLFW_PRESS,
// TODO: pass correct entity id once the missing systems are in place
0 );
0 );
}
old_packet = buffer;