mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 23:19:19 +02:00
milek7/sim branch network code import
This commit is contained in:
@@ -40,6 +40,7 @@ class memory_table;
|
||||
class powergridsource_table;
|
||||
class instance_table;
|
||||
class vehicle_table;
|
||||
class train_table;
|
||||
struct light_array;
|
||||
class particle_manager;
|
||||
struct dictionary_source;
|
||||
@@ -77,4 +78,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
|
||||
|
||||
@@ -1862,11 +1862,9 @@ 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
|
||||
}
|
||||
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
|
||||
}
|
||||
if( mvControlling->EngineType == TEngineType::DieselEngine ) {
|
||||
|
||||
57
DynObj.cpp
57
DynObj.cpp
@@ -1615,6 +1615,36 @@ TDynamicObject::~TDynamicObject() {
|
||||
SafeDeleteArray( pAnimated ); // lista animowanych submodeli
|
||||
}
|
||||
|
||||
void TDynamicObject::place_on_track(TTrack *Track, double fDist, bool Reversed)
|
||||
{
|
||||
for( auto &axle : m_axlesounds ) {
|
||||
// wyszukiwanie osi (0 jest na końcu, dlatego dodajemy długość?)
|
||||
axle.distance = (
|
||||
Reversed ?
|
||||
-axle.offset :
|
||||
( axle.offset + MoverParameters->Dim.L ) ) + fDist;
|
||||
}
|
||||
double fAxleDistHalf = fAxleDist * 0.5;
|
||||
// przesuwanie pojazdu tak, aby jego początek był we wskazanym miejcu
|
||||
fDist -= 0.5 * MoverParameters->Dim.L; // dodajemy pół długości pojazdu, bo ustawiamy jego środek (zliczanie na minus)
|
||||
switch (iNumAxles) {
|
||||
// Ra: pojazdy wstawiane są na tor początkowy, a potem przesuwane
|
||||
case 2: // ustawianie osi na torze
|
||||
Axle0.Init(Track, this, iDirection ? 1 : -1);
|
||||
Axle0.Reset();
|
||||
Axle0.Move((iDirection ? fDist : -fDist) + fAxleDistHalf, false);
|
||||
Axle1.Init(Track, this, iDirection ? 1 : -1);
|
||||
Axle1.Reset();
|
||||
Axle1.Move((iDirection ? fDist : -fDist) - fAxleDistHalf, false); // false, żeby nie generować eventów
|
||||
break;
|
||||
}
|
||||
// potrzebne do wyliczenia aktualnej pozycji; nie może być zero, bo nie przeliczy pozycji
|
||||
// teraz jeszcze trzeba przypisać pojazdy do nowego toru, bo przesuwanie początkowe osi nie
|
||||
// zrobiło tego
|
||||
Move( 0.0001 );
|
||||
ABuCheckMyTrack(); // zmiana toru na ten, co oś Axle0 (oś z przodu)
|
||||
}
|
||||
|
||||
double
|
||||
TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
|
||||
std::string BaseDir, // z którego katalogu wczytany, np. "PKP/EU07"
|
||||
@@ -2190,7 +2220,7 @@ TDynamicObject::create_controller( std::string const Type, bool const Trainset )
|
||||
|
||||
if( Type == "" ) { return; }
|
||||
|
||||
if( asName == Global.asHumanCtrlVehicle ) {
|
||||
if( asName == Global.local_start_vehicle ) {
|
||||
// jeśli pojazd wybrany do prowadzenia
|
||||
if( MoverParameters->EngineType != TEngineType::Dumb ) {
|
||||
// wsadzamy tam sterującego
|
||||
@@ -6542,6 +6572,20 @@ glm::dvec3 TDynamicObject::get_future_movement() const {
|
||||
return m_future_movement;
|
||||
}
|
||||
|
||||
void TDynamicObject::move_set(double distance)
|
||||
{
|
||||
TDynamicObject *d = this;
|
||||
while( d ) {
|
||||
d->Move( distance * d->DirectionGet() );
|
||||
d = d->Next(); // pozostałe też
|
||||
}
|
||||
d = Prev();
|
||||
while( d ) {
|
||||
d->Move( distance * d->DirectionGet() );
|
||||
d = d->Prev(); // w drugą stronę też
|
||||
}
|
||||
}
|
||||
|
||||
// returns type of the nearest functional power source present in the trainset
|
||||
TPowerSource
|
||||
TDynamicObject::ConnectedEnginePowerSource( TDynamicObject const *Caller ) const {
|
||||
@@ -6640,13 +6684,13 @@ TDynamicObject::update_shake( double const Timedelta ) {
|
||||
|
||||
auto shake { 1.25 * ShakeSpring.ComputateForces( shakevector, ShakeState.offset ) };
|
||||
|
||||
if( Random( iVel ) > 25.0 ) {
|
||||
if( LocalRandom( iVel ) > 25.0 ) {
|
||||
// extra shake at increased velocity
|
||||
shake += ShakeSpring.ComputateForces(
|
||||
Math3D::vector3(
|
||||
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.x,
|
||||
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.y,
|
||||
( Random( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.z )
|
||||
( LocalRandom( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.x,
|
||||
( LocalRandom( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.y,
|
||||
( LocalRandom( iVel * 2 ) - iVel ) / ( ( iVel * 2 ) * 4 ) * BaseShake.jolt_scale.z )
|
||||
// * (( 200 - DynamicObject->MyTrack->iQualityFlag ) * 0.0075 ) // scale to 75-150% based on track quality
|
||||
* 1.25,
|
||||
ShakeState.offset );
|
||||
@@ -6986,7 +7030,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub
|
||||
if( ( volume < 1.0 )
|
||||
&& ( Vehicle.EnginePower < 100 ) ) {
|
||||
|
||||
auto const volumevariation { Random( 100 ) * Vehicle.enrot / ( 1 + Vehicle.nmax ) };
|
||||
auto const volumevariation { LocalRandom( 100 ) * Vehicle.enrot / ( 1 + Vehicle.nmax ) };
|
||||
if( volumevariation < 2 ) {
|
||||
volume += volumevariation / 200;
|
||||
}
|
||||
@@ -7362,6 +7406,7 @@ vehicle_table::erase_disabled() {
|
||||
// TBD, TODO: kill vehicle sounds
|
||||
SafeDelete( simulation::Train );
|
||||
}
|
||||
simulation::Trains.purge(vehicle->name());
|
||||
// remove potential entries in the light array
|
||||
simulation::Lights.remove( vehicle );
|
||||
/*
|
||||
|
||||
2
DynObj.h
2
DynObj.h
@@ -544,6 +544,7 @@ private:
|
||||
|
||||
TDynamicObject();
|
||||
~TDynamicObject();
|
||||
void place_on_track(TTrack *Track, double fDist, bool Reversed);
|
||||
// zwraca długość pojazdu albo 0, jeśli błąd
|
||||
double Init(
|
||||
std::string Name, std::string BaseDir, std::string asReplacableSkin, std::string Type_Name,
|
||||
@@ -672,6 +673,7 @@ private:
|
||||
material_handle DestinationFind( std::string Destination );
|
||||
void OverheadTrack(float o);
|
||||
glm::dvec3 get_future_movement() const;
|
||||
void move_set(double distance);
|
||||
|
||||
double MED[9][8]; // lista zmiennych do debugowania hamulca ED
|
||||
static std::string const MED_labels[ 8 ];
|
||||
|
||||
64
EvLaunch.cpp
64
EvLaunch.cpp
@@ -31,20 +31,23 @@ http://mozilla.org/MPL/2.0/.
|
||||
// encodes expected key in a short, where low byte represents the actual key,
|
||||
// and the high byte holds modifiers: 0x1 = shift, 0x2 = ctrl, 0x4 = alt
|
||||
int vk_to_glfw_key( int const Keycode ) {
|
||||
char modifier = 0;
|
||||
char key = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
auto const code = VkKeyScan( Keycode );
|
||||
char key = code & 0xff;
|
||||
char shiftstate = ( code & 0xff00 ) >> 8;
|
||||
if (Keycode < 'A') {
|
||||
key = Keycode;
|
||||
} else if (Keycode <= 'Z') {
|
||||
key = Keycode;
|
||||
modifier = GLFW_MOD_SHIFT;
|
||||
} else if (Keycode < 'a') {
|
||||
key = Keycode;
|
||||
} else if (Keycode <= 'z') {
|
||||
key = Keycode - 32;
|
||||
} else {
|
||||
ErrorLog("unknown key: " + std::to_string(Keycode));
|
||||
}
|
||||
|
||||
if( (key >= 'A') && (key <= 'Z') ) {
|
||||
key = GLFW_KEY_A + key - 'A';
|
||||
}
|
||||
else if( ( key >= '0' ) && ( key <= '9' ) ) {
|
||||
key = GLFW_KEY_0 + key - '0';
|
||||
}
|
||||
return key + ( shiftstate << 8 );
|
||||
#endif
|
||||
return ((int)modifier << 8) | key;
|
||||
}
|
||||
|
||||
bool TEventLauncher::Load(cParser *parser)
|
||||
@@ -152,24 +155,33 @@ bool TEventLauncher::Load(cParser *parser)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TEventLauncher::check_activation_key() {
|
||||
if (iKey <= 0)
|
||||
return false;
|
||||
|
||||
char key = iKey & 0xff;
|
||||
|
||||
bool result = Console::Pressed(key);
|
||||
|
||||
char modifier = iKey >> 8;
|
||||
if (modifier & GLFW_MOD_SHIFT)
|
||||
result &= Global.shiftState;
|
||||
if (modifier & GLFW_MOD_CONTROL)
|
||||
result &= Global.ctrlState;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TEventLauncher::check_activation() {
|
||||
|
||||
auto bCond { false };
|
||||
|
||||
if( iKey > 0 ) {
|
||||
if( iKey > 255 ) {
|
||||
// key and modifier
|
||||
auto const modifier = ( iKey & 0xff00 ) >> 8;
|
||||
bCond = ( Console::Pressed( iKey & 0xff ) )
|
||||
&& ( ( modifier & 1 ) ? Global.shiftState : true )
|
||||
&& ( ( modifier & 2 ) ? Global.ctrlState : true );
|
||||
}
|
||||
else {
|
||||
// just key
|
||||
bCond = ( Console::Pressed( iKey & 0xff ) ); // czy klawisz wciśnięty
|
||||
}
|
||||
}
|
||||
if( DeltaTime > 0 ) {
|
||||
if (DeltaTime == 10000.0) {
|
||||
if (UpdatedTime == 0.0)
|
||||
bCond = true;
|
||||
UpdatedTime = 1.0;
|
||||
}
|
||||
else if( DeltaTime > 0 ) {
|
||||
if( UpdatedTime > DeltaTime ) {
|
||||
UpdatedTime = 0; // naliczanie od nowa
|
||||
bCond = true;
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
|
||||
// methods
|
||||
bool Load( cParser *parser );
|
||||
bool check_activation_key();
|
||||
bool check_activation();
|
||||
// checks conditions associated with the event. returns: true if the conditions are met
|
||||
bool check_conditions();
|
||||
|
||||
19
Event.cpp
19
Event.cpp
@@ -1966,14 +1966,19 @@ event_manager::update() {
|
||||
CheckQuery();
|
||||
// test list of global events for possible new additions to the queue
|
||||
for( auto *launcher : m_launcherqueue ) {
|
||||
if (launcher->check_conditions() && launcher->Event1) {
|
||||
// NOTE: we're presuming global events aren't going to use event2
|
||||
|
||||
if( true == ( launcher->check_activation() && launcher->check_conditions() ) ) {
|
||||
// NOTE: we're presuming global events aren't going to use event2
|
||||
WriteLog( "Eventlauncher " + launcher->name() );
|
||||
if( launcher->Event1 ) {
|
||||
AddToQuery( launcher->Event1, nullptr );
|
||||
}
|
||||
}
|
||||
if (launcher->check_activation()) {
|
||||
WriteLog( "Eventlauncher: " + launcher->name() );
|
||||
AddToQuery( launcher->Event1, nullptr );
|
||||
}
|
||||
|
||||
if (launcher->check_activation_key()) {
|
||||
WriteLog( "Eventlauncher: " + launcher->name() );
|
||||
m_relay.post(user_command::queueevent, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &launcher->Event1->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
Event.h
6
Event.h
@@ -77,6 +77,7 @@ public:
|
||||
virtual glm::dvec3 input_location() const;
|
||||
void group( scene::group_handle Group );
|
||||
scene::group_handle group() const;
|
||||
std::string const &name() const { return m_name; }
|
||||
// members
|
||||
basic_event *m_next { nullptr }; // następny w kolejce // TODO: replace with event list in the manager
|
||||
basic_event *m_sibling { nullptr }; // kolejny event z tą samą nazwą - od wersji 378
|
||||
@@ -584,6 +585,10 @@ public:
|
||||
// legacy method, returns pointer to specified event, or null
|
||||
basic_event *
|
||||
FindEvent( std::string const &Name );
|
||||
inline TEventLauncher* FindEventlauncher(std::string const &Name) {
|
||||
auto ptr = m_inputdrivenlaunchers.find(Name);
|
||||
return ptr ? ptr : m_radiodrivenlaunchers.find(Name);
|
||||
}
|
||||
// legacy method, inserts specified event in the event query
|
||||
bool
|
||||
AddToQuery( basic_event *Event, TDynamicObject const *Owner );
|
||||
@@ -618,6 +623,7 @@ private:
|
||||
basic_table<TEventLauncher> m_inputdrivenlaunchers;
|
||||
basic_table<TEventLauncher> m_radiodrivenlaunchers;
|
||||
eventlauncher_sequence m_launcherqueue;
|
||||
command_relay m_relay;
|
||||
};
|
||||
|
||||
|
||||
|
||||
37
Globals.cpp
37
Globals.cpp
@@ -52,7 +52,7 @@ global_settings::ConfigParse(cParser &Parser) {
|
||||
{
|
||||
|
||||
Parser.getTokens();
|
||||
Parser >> asHumanCtrlVehicle;
|
||||
Parser >> local_start_vehicle;
|
||||
}
|
||||
else if( token == "fieldofview" ) {
|
||||
|
||||
@@ -768,6 +768,18 @@ global_settings::ConfigParse(cParser &Parser) {
|
||||
Parser.getTokens(1);
|
||||
Parser >> gfx_shadowmap_enabled;
|
||||
}
|
||||
else if (token == "fpslimit")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
float fpslimit;
|
||||
Parser >> fpslimit;
|
||||
minframetime = std::chrono::duration<float>(1.0f / fpslimit);
|
||||
}
|
||||
else if (token == "randomseed")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
Parser >> Global.random_seed;
|
||||
}
|
||||
else if (token == "gfx.envmap.enabled")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
@@ -852,6 +864,29 @@ global_settings::ConfigParse(cParser &Parser) {
|
||||
Parser.getTokens(1);
|
||||
Parser >> python_mipmaps;
|
||||
}
|
||||
else if (token == "network.server")
|
||||
{
|
||||
Parser.getTokens(2);
|
||||
|
||||
std::string backend;
|
||||
std::string conf;
|
||||
Parser >> backend >> conf;
|
||||
|
||||
network_servers.push_back(std::make_pair(backend, conf));
|
||||
}
|
||||
else if (token == "network.client")
|
||||
{
|
||||
Parser.getTokens(2);
|
||||
|
||||
network_client.emplace();
|
||||
Parser >> network_client->first;
|
||||
Parser >> network_client->second;
|
||||
}
|
||||
else if (token == "execonexit") {
|
||||
Parser.getTokens(1);
|
||||
Parser >> exec_on_exit;
|
||||
std::replace(std::begin(exec_on_exit), std::end(exec_on_exit), '_', ' ');
|
||||
}
|
||||
/*
|
||||
else if (token == "crashdamage") {
|
||||
Parser.getTokens(1);
|
||||
|
||||
24
Globals.h
24
Globals.h
@@ -28,8 +28,11 @@ struct global_settings {
|
||||
bool shiftState{ false }; //m7todo: brzydko
|
||||
bool ctrlState{ false };
|
||||
bool altState{ false };
|
||||
std::mt19937 random_engine{ std::mt19937( static_cast<unsigned int>( std::time( NULL ) ) ) };
|
||||
std::mt19937 local_random_engine{ std::mt19937( static_cast<unsigned int>( std::time( NULL ) ) ) };
|
||||
std::mt19937 random_engine;
|
||||
std::mt19937 local_random_engine;
|
||||
bool ready_to_load { false };
|
||||
std::time_t starting_timestamp = 0; // starting time, in local timezone
|
||||
uint32_t random_seed = 0;
|
||||
TDynamicObject *changeDynObj{ nullptr };// info o zmianie pojazdu
|
||||
TCamera pCamera; // parametry kamery
|
||||
TCamera pDebugCamera;
|
||||
@@ -63,7 +66,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
|
||||
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
|
||||
// logs
|
||||
@@ -188,6 +191,13 @@ struct global_settings {
|
||||
std::string asVersion{ "UNKNOWN" }; // z opisem
|
||||
// TODO: move these to relevant areas
|
||||
bool render_cab = true;
|
||||
|
||||
std::chrono::duration<float> minframetime {0.0f};
|
||||
|
||||
std::string fullscreen_monitor;
|
||||
|
||||
bool python_mipmaps = true;
|
||||
|
||||
int gfx_framebuffer_width = -1;
|
||||
int gfx_framebuffer_height = -1;
|
||||
int gfx_framebuffer_fidelity = -1;
|
||||
@@ -202,8 +212,12 @@ struct global_settings {
|
||||
bool gfx_extraeffects = true;
|
||||
bool gfx_shadergamma = false;
|
||||
bool gfx_usegles = false;
|
||||
std::string fullscreen_monitor;
|
||||
bool python_mipmaps = true;
|
||||
|
||||
std::string exec_on_exit;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> network_servers;
|
||||
std::optional<std::pair<std::string, std::string>> network_client;
|
||||
double desync = 0.0;
|
||||
|
||||
// methods
|
||||
void LoadIniFile( std::string asFileName );
|
||||
|
||||
2
Logs.h
2
Logs.h
@@ -20,7 +20,7 @@ enum logtype : unsigned int {
|
||||
// lua = ( 1 << 4 ),
|
||||
material = ( 1 << 5 ),
|
||||
shader = ( 1 << 6 ),
|
||||
// net = ( 1 << 7 )
|
||||
net = ( 1 << 7 )
|
||||
};
|
||||
|
||||
void WriteLog( const char *str, logtype const Type = logtype::generic );
|
||||
|
||||
37
Names.h
37
Names.h
@@ -38,6 +38,43 @@ public:
|
||||
// item with this name already exists; update mapping to point to the new one, for backward compatibility
|
||||
mapping.first->second = itemhandle;
|
||||
return false; }
|
||||
void purge (std::string const &Name)
|
||||
{
|
||||
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?
|
||||
|
||||
m_itemmap.erase(lookup);
|
||||
}
|
||||
uint32_t find_id( std::string const &Name) const {
|
||||
auto lookup = m_itemmap.find( Name );
|
||||
return (
|
||||
lookup != m_itemmap.end() ?
|
||||
lookup->second :
|
||||
-1 );
|
||||
}
|
||||
void purge (Type_ *Item)
|
||||
{
|
||||
for (auto it = m_items.begin(); it != m_items.end(); it++) {
|
||||
if (*it == Item) {
|
||||
delete *it;
|
||||
*it = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// locates item with specified name. returns pointer to the item, or nullptr
|
||||
Type_ *
|
||||
find( std::string const &Name ) const {
|
||||
|
||||
46
Timer.cpp
46
Timer.cpp
@@ -15,13 +15,14 @@ namespace Timer {
|
||||
|
||||
subsystem_stopwatches subsystem;
|
||||
|
||||
double DeltaTime, DeltaRenderTime;
|
||||
double DeltaTime = 0.0, DeltaRenderTime = 0.0;
|
||||
double fFPS{ 0.0f };
|
||||
double fLastTime{ 0.0f };
|
||||
DWORD dwFrames{ 0 };
|
||||
double fSimulationTime{ 0.0 };
|
||||
double fSoundTimer{ 0.0 };
|
||||
double fSinceStart{ 0.0 };
|
||||
double override_delta = -1.0f;
|
||||
|
||||
double GetTime()
|
||||
{
|
||||
@@ -30,6 +31,8 @@ double GetTime()
|
||||
|
||||
double GetDeltaTime()
|
||||
{ // czas symulacji (stoi gdy pauza)
|
||||
if (override_delta != -1.0f)
|
||||
return override_delta;
|
||||
return DeltaTime;
|
||||
}
|
||||
|
||||
@@ -38,35 +41,31 @@ 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()
|
||||
{
|
||||
UpdateTimers( Global.iPause != 0 );
|
||||
DeltaTime = 0.1;
|
||||
DeltaTime = 0.0;
|
||||
DeltaRenderTime = 0.0;
|
||||
fSoundTimer = 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
LONGLONG fr, count, oldCount;
|
||||
|
||||
void UpdateTimers(bool pause) {
|
||||
uint64_t fr, count, oldCount;
|
||||
|
||||
void UpdateTimers(bool pause)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&fr);
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&count);
|
||||
#elif __unix__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
count = (uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec;
|
||||
fr = 1000000000;
|
||||
#endif
|
||||
DeltaRenderTime = double(count - oldCount) / double(fr);
|
||||
if (!pause)
|
||||
{
|
||||
@@ -77,15 +76,19 @@ void UpdateTimers(bool pause) {
|
||||
|
||||
if (DeltaTime > 1.0)
|
||||
DeltaTime = 1.0;
|
||||
|
||||
fSimulationTime += GetDeltaTime();
|
||||
}
|
||||
else
|
||||
DeltaTime = 0.0; // wszystko stoi, bo czas nie płynie
|
||||
|
||||
oldCount = count;
|
||||
// Keep track of the time lapse and frame count
|
||||
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||
#if __unix__
|
||||
double fTime = (double)(count / 1000000000);
|
||||
#elif _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
||||
double fTime = ::GetTickCount64() * 0.001f; // Get current time in seconds
|
||||
#else
|
||||
#elif _WIN32
|
||||
double fTime = ::GetTickCount() * 0.001f; // Get current time in seconds
|
||||
#endif
|
||||
++dwFrames; // licznik ramek
|
||||
@@ -96,7 +99,6 @@ void UpdateTimers(bool pause) {
|
||||
fLastTime = fTime;
|
||||
dwFrames = 0L;
|
||||
}
|
||||
fSimulationTime += DeltaTime;
|
||||
};
|
||||
|
||||
}; // namespace timer
|
||||
|
||||
16
Timer.h
16
Timer.h
@@ -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();
|
||||
|
||||
@@ -35,17 +31,23 @@ public:
|
||||
void
|
||||
start() {
|
||||
m_start = std::chrono::steady_clock::now(); }
|
||||
void
|
||||
std::chrono::duration<float, std::milli>
|
||||
stop() {
|
||||
m_accumulator = 0.95f * m_accumulator + std::chrono::duration_cast<std::chrono::microseconds>( ( std::chrono::steady_clock::now() - m_start ) ).count() / 1000.f; }
|
||||
m_last = std::chrono::duration_cast<std::chrono::microseconds>( ( std::chrono::steady_clock::now() - m_start ) );
|
||||
m_accumulator = 0.95f * m_accumulator + m_last.count() / 1000.f;
|
||||
return m_last; }
|
||||
float
|
||||
average() const {
|
||||
return m_accumulator / 20.f;}
|
||||
std::chrono::microseconds
|
||||
last() const {
|
||||
return m_last; }
|
||||
|
||||
private:
|
||||
// members
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_start { std::chrono::steady_clock::now() };
|
||||
float m_accumulator { 1000.f / 30.f * 20.f }; // 20 last samples, initial 'neutral' rate of 30 fps
|
||||
std::chrono::microseconds m_last;
|
||||
};
|
||||
|
||||
struct subsystem_stopwatches {
|
||||
|
||||
166
Train.cpp
166
Train.cpp
@@ -381,6 +381,9 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = {
|
||||
{ user_command::generictoggle7, &TTrain::OnCommand_generictoggle },
|
||||
{ user_command::generictoggle8, &TTrain::OnCommand_generictoggle },
|
||||
{ user_command::generictoggle9, &TTrain::OnCommand_generictoggle },
|
||||
{ user_command::vehiclemoveforwards, &TTrain::OnCommand_vehiclemoveforwards },
|
||||
{ user_command::vehiclemovebackwards, &TTrain::OnCommand_vehiclemovebackwards },
|
||||
{ user_command::vehicleboost, &TTrain::OnCommand_vehicleboost },
|
||||
{ user_command::springbraketoggle, &TTrain::OnCommand_springbraketoggle },
|
||||
{ user_command::springbrakeenable, &TTrain::OnCommand_springbrakeenable },
|
||||
{ user_command::springbrakedisable, &TTrain::OnCommand_springbrakedisable },
|
||||
@@ -5582,14 +5585,15 @@ void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Comm
|
||||
end::rear ) };
|
||||
if( TestFlag( Train->DynamicObject->MoverParameters->Couplers[ exitdirection ].CouplingFlag, coupling::gangway ) ) {
|
||||
// przejscie do nastepnego pojazdu
|
||||
Global.changeDynObj = (
|
||||
auto *targetvehicle = (
|
||||
exitdirection == end::front ?
|
||||
Train->DynamicObject->PrevConnected() :
|
||||
Train->DynamicObject->NextConnected() );
|
||||
Global.changeDynObj->MoverParameters->CabOccupied = (
|
||||
targetvehicle->MoverParameters->CabOccupied = (
|
||||
Train->DynamicObject->MoverParameters->Neighbours[ exitdirection ].vehicle_end ?
|
||||
-1 :
|
||||
1 );
|
||||
Train->MoveToVehicle( targetvehicle );
|
||||
}
|
||||
}
|
||||
// HACK: match consist door permit state with the preset in the active cab
|
||||
@@ -5615,14 +5619,15 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com
|
||||
end::rear ) };
|
||||
if( TestFlag( Train->DynamicObject->MoverParameters->Couplers[ exitdirection ].CouplingFlag, coupling::gangway ) ) {
|
||||
// przejscie do nastepnego pojazdu
|
||||
Global.changeDynObj = (
|
||||
auto *targetvehicle = (
|
||||
exitdirection == end::front ?
|
||||
Train->DynamicObject->PrevConnected() :
|
||||
Train->DynamicObject->NextConnected() );
|
||||
Global.changeDynObj->MoverParameters->CabOccupied = (
|
||||
targetvehicle->MoverParameters->CabOccupied = (
|
||||
Train->DynamicObject->MoverParameters->Neighbours[ exitdirection ].vehicle_end ?
|
||||
-1 :
|
||||
1 );
|
||||
Train->MoveToVehicle( targetvehicle );
|
||||
}
|
||||
}
|
||||
// HACK: match consist door permit state with the preset in the active cab
|
||||
@@ -5632,6 +5637,38 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com
|
||||
}
|
||||
}
|
||||
|
||||
void TTrain::OnCommand_vehiclemoveforwards(TTrain *Train, const command_data &Command) {
|
||||
if (Command.action == GLFW_RELEASE || !DebugModeFlag)
|
||||
return;
|
||||
|
||||
Train->DynamicObject->move_set(100.0);
|
||||
}
|
||||
|
||||
void TTrain::OnCommand_vehiclemovebackwards(TTrain *Train, const command_data &Command) {
|
||||
if (Command.action == GLFW_RELEASE || !DebugModeFlag)
|
||||
return;
|
||||
|
||||
Train->DynamicObject->move_set(-100.0);
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -5720,7 +5757,7 @@ bool TTrain::Update( double const Deltatime )
|
||||
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, static_cast<std::size_t>( command_target::vehicle ) | id() ) ) {
|
||||
|
||||
auto lookup = m_commandhandlers.find( commanddata.command );
|
||||
if( lookup != m_commandhandlers.end() ) {
|
||||
@@ -7829,6 +7866,95 @@ void TTrain::DynamicSet(TDynamicObject *d)
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
TTrain::MoveToVehicle(TDynamicObject *target) {
|
||||
// > Ra: to nie może być tak robione, to zbytnia proteza jest
|
||||
// indeed, too much hacks...
|
||||
// TODO: cleanup
|
||||
|
||||
TTrain *target_train = simulation::Trains.find(target->name());
|
||||
if (target_train) {
|
||||
// let's try to destroy this TTrain and move to already existing one
|
||||
|
||||
if (!Dynamic()->Mechanik || !Dynamic()->Mechanik->AIControllFlag) {
|
||||
// tylko jeśli ręcznie prowadzony
|
||||
// jeśli prowadzi AI, to mu nie robimy dywersji!
|
||||
Occupied()->CabDeactivisation();
|
||||
Occupied()->CabOccupied = 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( {} );
|
||||
|
||||
Dynamic()->Mechanik->MoveTo(target);
|
||||
|
||||
target_train->Occupied()->LimPipePress = target_train->Occupied()->PipePress;
|
||||
target_train->Occupied()->CabActivisation(); // załączenie rozrządu (wirtualne kabiny)
|
||||
target_train->Dynamic()->MechInside = true;
|
||||
target_train->Dynamic()->Controller = Humandriver;
|
||||
} else {
|
||||
target_train->Dynamic()->bDisplayCab = false;
|
||||
target_train->Dynamic()->ABuSetModelShake( {} );
|
||||
}
|
||||
|
||||
target_train->Dynamic()->bDisplayCab = true;
|
||||
target_train->Dynamic()->ABuSetModelShake( {} ); // zerowanie przesunięcia przed powrotem?
|
||||
|
||||
// potentially move player
|
||||
if (simulation::Train == this) {
|
||||
simulation::Train = target_train;
|
||||
}
|
||||
|
||||
// delete this TTrain
|
||||
pending_delete = true;
|
||||
} else {
|
||||
// move this TTrain to other dynamic
|
||||
|
||||
// remove TTrain from global list, we're going to change dynamic anyway
|
||||
simulation::Trains.detach(Dynamic()->name());
|
||||
|
||||
if (!Dynamic()->Mechanik || !Dynamic()->Mechanik->AIControllFlag) {
|
||||
// tylko jeśli ręcznie prowadzony
|
||||
// jeśli prowadzi AI, to mu nie robimy dywersji!
|
||||
|
||||
Occupied()->CabDeactivisation();
|
||||
Occupied()->CabOccupied = 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( {} );
|
||||
|
||||
Dynamic()->Mechanik->MoveTo(target);
|
||||
|
||||
DynamicSet(target);
|
||||
|
||||
Occupied()->LimPipePress = Occupied()->PipePress;
|
||||
Occupied()->CabActivisation(); // załączenie rozrządu (wirtualne kabiny)
|
||||
Dynamic()->MechInside = true;
|
||||
Dynamic()->Controller = Humandriver;
|
||||
} else {
|
||||
Dynamic()->bDisplayCab = false;
|
||||
Dynamic()->ABuSetModelShake( {} );
|
||||
|
||||
DynamicSet(target);
|
||||
}
|
||||
|
||||
InitializeCab(
|
||||
Occupied()->CabActive,
|
||||
Dynamic()->asBaseDir + Occupied()->TypeName + ".mmd" );
|
||||
|
||||
Dynamic()->bDisplayCab = true;
|
||||
Dynamic()->ABuSetModelShake( {} ); // zerowanie przesunięcia przed powrotem?
|
||||
|
||||
// add it back with updated dynamic name
|
||||
simulation::Trains.insert(this);
|
||||
}
|
||||
}
|
||||
|
||||
// checks whether specified point is within boundaries of the active cab
|
||||
bool
|
||||
TTrain::point_inside( Math3D::vector3 const Point ) const {
|
||||
@@ -9106,3 +9232,33 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t TTrain::id() {
|
||||
if (vid == 0) {
|
||||
vid = ++simulation::prev_train_id;
|
||||
WriteLog("net: assigning id " + std::to_string(vid) + " to vehicle " + Dynamic()->name(), logtype::net);
|
||||
}
|
||||
return vid;
|
||||
}
|
||||
|
||||
void train_table::update(double dt)
|
||||
{
|
||||
for (TTrain *train : m_items) {
|
||||
if (!train)
|
||||
continue;
|
||||
|
||||
train->Update(dt);
|
||||
|
||||
if (train->pending_delete) {
|
||||
purge(train->Dynamic()->name());
|
||||
if (simulation::Train == train)
|
||||
simulation::Train = nullptr;
|
||||
}
|
||||
|
||||
// for single-player destroy non-player trains
|
||||
if (simulation::Train != train
|
||||
&& Global.network_servers.empty() && !Global.network_client) {
|
||||
purge(train->Dynamic()->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
Train.h
16
Train.h
@@ -130,6 +130,9 @@ class TTrain {
|
||||
bool LoadMMediaFile(std::string const &asFileName);
|
||||
dictionary_source *GetTrainState();
|
||||
state_t get_state() const;
|
||||
inline
|
||||
std::string name() const {
|
||||
return Dynamic()->name(); }
|
||||
|
||||
private:
|
||||
// types
|
||||
@@ -384,6 +387,9 @@ 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_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 );
|
||||
static void OnCommand_springbraketoggle(TTrain *Train, command_data const &Command);
|
||||
static void OnCommand_springbrakeenable(TTrain *Train, command_data const &Command);
|
||||
static void OnCommand_springbrakedisable(TTrain *Train, command_data const &Command);
|
||||
@@ -754,6 +760,7 @@ private:
|
||||
bool m_mastercontrollerinuse { false };
|
||||
float m_mastercontrollerreturndelay { 0.f };
|
||||
std::vector<std::pair<std::string, texture_handle>> m_screens;
|
||||
uint16_t vid { 0 }; // train network recipient id
|
||||
float m_distancecounter { -1.f }; // distance traveled since meter was activated or -1 if inactive
|
||||
double m_brakehandlecp{ 0.0 };
|
||||
int m_pantselection{ 0 };
|
||||
@@ -775,9 +782,18 @@ 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;
|
||||
|
||||
uint16_t id();
|
||||
bool pending_delete = false;
|
||||
};
|
||||
|
||||
class train_table : public basic_table<TTrain> {
|
||||
public:
|
||||
void update( double dt );
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -38,6 +38,12 @@ bool TTrackFollower::Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TTrackFollower::Reset()
|
||||
{
|
||||
fCurrentDistance = 0.0;
|
||||
fDirection = 1.0;
|
||||
}
|
||||
|
||||
TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end)
|
||||
{ // przejechanie na inny odcinkek toru, z ewentualnym rozpruciem
|
||||
if (pTrack)
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
//{ return pCurrentSegment->ComputeLength(p1,cp1,cp2,p2); };
|
||||
// inline double GetRadius(double L, double d); //McZapkie-150503
|
||||
bool Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir);
|
||||
void Reset();
|
||||
void Render(float fNr);
|
||||
// members
|
||||
double fOffsetH = 0.0; // Ra: odległość środka osi od osi toru (dla samochodów) - użyć do wężykowania
|
||||
|
||||
263
application.cpp
263
application.cpp
@@ -43,6 +43,11 @@ http://mozilla.org/MPL/2.0/.
|
||||
#pragma comment (lib, "dbghelp.lib")
|
||||
#pragma comment (lib, "version.lib")
|
||||
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
extern "C" { _declspec( dllexport ) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; }
|
||||
extern "C" { _declspec( dllexport ) DWORD NvOptimusEnablement = 0x00000001; }
|
||||
|
||||
@@ -65,19 +70,14 @@ extern WNDPROC BaseWindowProc;
|
||||
// user input callbacks
|
||||
|
||||
void focus_callback( GLFWwindow *window, int focus ) {
|
||||
if( Global.bInactivePause ) // jeśli ma być pauzowanie okna w tle
|
||||
if( focus )
|
||||
Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie
|
||||
else
|
||||
Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy
|
||||
Application.on_focus_change(focus != 0);
|
||||
}
|
||||
|
||||
void window_resize_callback( GLFWwindow *window, int w, int h ) {
|
||||
// NOTE: we have two variables which basically do the same thing as we don't have dynamic fullscreen toggle
|
||||
// TBD, TODO: merge them?
|
||||
Global.iWindowWidth = w;
|
||||
Global.iWindowHeight = h;
|
||||
Global.fDistanceFactor = std::max( 0.5f, h / 768.0f ); // not sure if this is really something we want to use
|
||||
Global.iWindowHeight = h;
|
||||
glViewport( 0, 0, w, h );
|
||||
}
|
||||
|
||||
@@ -101,6 +101,11 @@ void key_callback( GLFWwindow *window, int key, int scancode, int action, int mo
|
||||
Application.on_key( key, scancode, action, mods );
|
||||
}
|
||||
|
||||
void char_callback(GLFWwindow *window, unsigned int c)
|
||||
{
|
||||
Application.on_char(c);
|
||||
}
|
||||
|
||||
// public:
|
||||
|
||||
int
|
||||
@@ -117,10 +122,10 @@ eu07_application::init( int Argc, char *Argv[] ) {
|
||||
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" );
|
||||
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_glfw() ) != 0 ) {
|
||||
return result;
|
||||
@@ -140,9 +145,28 @@ eu07_application::init( int Argc, char *Argv[] ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!init_network())
|
||||
return -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double eu07_application::generate_sync() {
|
||||
if (Timer::GetDeltaTime() == 0.0)
|
||||
return 0.0;
|
||||
double sync = 0.0;
|
||||
for (const TDynamicObject* vehicle : simulation::Vehicles.sequence()) {
|
||||
auto const pos { vehicle->GetPosition() };
|
||||
sync += pos.x + pos.y + pos.z;
|
||||
}
|
||||
sync += Random(1.0, 100.0);
|
||||
return sync;
|
||||
}
|
||||
|
||||
void eu07_application::queue_quit() {
|
||||
glfwSetWindowShouldClose(m_windows[0], GLFW_TRUE);
|
||||
}
|
||||
|
||||
int
|
||||
eu07_application::run() {
|
||||
|
||||
@@ -151,11 +175,125 @@ eu07_application::run() {
|
||||
{
|
||||
Timer::subsystem.mainloop_total.start();
|
||||
|
||||
if( !m_modes[ m_modestack.top() ]->update() )
|
||||
break;
|
||||
// -------------------------------------------------------------------
|
||||
// multiplayer command relaying logic can seem a bit complex
|
||||
//
|
||||
// we are simultaneously:
|
||||
// => master (not client) OR slave (client)
|
||||
// => server OR not
|
||||
//
|
||||
// trivia: being client and server is possible
|
||||
|
||||
double frameStartTime = Timer::GetTime();
|
||||
|
||||
if (m_modes[m_modestack.top()]->is_command_processor()) {
|
||||
// active mode is doing real calculations (e.g. drivermode)
|
||||
int loop_remaining = MAX_NETWORK_PER_FRAME;
|
||||
while (--loop_remaining > 0)
|
||||
{
|
||||
command_queue::commands_map commands_to_exec;
|
||||
command_queue::commands_map local_commands = simulation::Commands.pop_intercept_queue();
|
||||
double slave_sync;
|
||||
|
||||
// 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();
|
||||
|
||||
// push these into local queue
|
||||
add_to_dequemap(local_commands, remote_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(MAX_NETWORK_PER_FRAME - loop_remaining);
|
||||
|
||||
// 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)
|
||||
loop_remaining = -1;
|
||||
}
|
||||
// if we're master
|
||||
else {
|
||||
// just push local commands to execution
|
||||
add_to_dequemap(commands_to_exec, local_commands);
|
||||
|
||||
loop_remaining = -1;
|
||||
}
|
||||
|
||||
// send commands to command queue
|
||||
simulation::Commands.push_commands(commands_to_exec);
|
||||
|
||||
// do actual frame processing
|
||||
if (!m_modes[ m_modestack.top() ]->update())
|
||||
return 0;
|
||||
|
||||
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();
|
||||
double render = Timer::GetDeltaRenderTime();
|
||||
m_network->servers->push_delta(render, delta, sync, commands_to_exec);
|
||||
}
|
||||
|
||||
// if we're slave
|
||||
if (m_network && m_network->client)
|
||||
{
|
||||
// verify sync
|
||||
if (sync != slave_sync) {
|
||||
WriteLog("net: desync! calculated: " + std::to_string(sync)
|
||||
+ ", received: " + std::to_string(slave_sync), logtype::net);
|
||||
|
||||
Global.desync = slave_sync - sync;
|
||||
}
|
||||
|
||||
// set total delta for rendering code
|
||||
double totalDelta = Timer::GetTime() - frameStartTime;
|
||||
Timer::set_delta_override(totalDelta);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loop_remaining) {
|
||||
// loop break forced by counter
|
||||
float received = m_network->client->get_frame_counter();
|
||||
float awaiting = m_network->client->get_awaiting_frames();
|
||||
|
||||
// TODO: don't meddle with mode progresbar
|
||||
m_modes[m_modestack.top()]->set_progress(100.0f, 100.0f * (received - awaiting) / received);
|
||||
} else {
|
||||
m_modes[m_modestack.top()]->set_progress(0.0f, 0.0f);
|
||||
}
|
||||
} 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())
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// TODO: re-enable after python changes merge
|
||||
// m_taskqueue.update();
|
||||
|
||||
if (!GfxRenderer->Render())
|
||||
break;
|
||||
return 0;
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
@@ -164,10 +302,15 @@ eu07_application::run() {
|
||||
|
||||
m_modes[ m_modestack.top() ]->on_event_poll();
|
||||
|
||||
Timer::subsystem.mainloop_total.stop();
|
||||
if (m_network)
|
||||
m_network->update();
|
||||
|
||||
auto frametime = Timer::subsystem.mainloop_total.stop();
|
||||
if (Global.minframetime.count() != 0.0f && (Global.minframetime - frametime).count() > 0.0f)
|
||||
std::this_thread::sleep_for(Global.minframetime - frametime);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
|
||||
@@ -199,8 +342,12 @@ eu07_application::release_python_lock() {
|
||||
|
||||
void
|
||||
eu07_application::exit() {
|
||||
for (auto &mode : m_modes)
|
||||
mode.reset();
|
||||
|
||||
SafeDelete( simulation::Train );
|
||||
m_network.reset();
|
||||
|
||||
// SafeDelete( simulation::Train );
|
||||
SafeDelete( simulation::Region );
|
||||
|
||||
ui_layer::shutdown();
|
||||
@@ -208,8 +355,11 @@ eu07_application::exit() {
|
||||
for( auto *window : m_windows ) {
|
||||
glfwDestroyWindow( window );
|
||||
}
|
||||
glfwTerminate();
|
||||
m_taskqueue.exit();
|
||||
glfwTerminate();
|
||||
|
||||
if (!Global.exec_on_exit.empty())
|
||||
system(Global.exec_on_exit.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -222,7 +372,6 @@ eu07_application::render_ui() {
|
||||
|
||||
bool
|
||||
eu07_application::pop_mode() {
|
||||
|
||||
if( m_modestack.empty() ) { return false; }
|
||||
|
||||
m_modes[ m_modestack.top() ]->exit();
|
||||
@@ -291,6 +440,7 @@ eu07_application::get_cursor_pos( double &Horizontal, double &Vertical ) const {
|
||||
glfwGetCursorPos( m_windows.front(), &Horizontal, &Vertical );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
eu07_application::get_mouse_button( int const Button ) const {
|
||||
|
||||
@@ -314,6 +464,13 @@ eu07_application::on_key( int const Key, int const Scancode, int const Action, i
|
||||
m_modes[ m_modestack.top() ]->on_key( Key, Scancode, Action, Mods );
|
||||
}
|
||||
|
||||
void eu07_application::on_char( unsigned int const Char ) {
|
||||
|
||||
if( m_modestack.empty() ) { return; }
|
||||
|
||||
m_modes[ m_modestack.top() ]->on_char( Char );
|
||||
}
|
||||
|
||||
void
|
||||
eu07_application::on_cursor_pos( double const Horizontal, double const Vertical ) {
|
||||
|
||||
@@ -338,6 +495,13 @@ eu07_application::on_scroll( double const Xoffset, double const Yoffset ) {
|
||||
m_modes[ m_modestack.top() ]->on_scroll( Xoffset, Yoffset );
|
||||
}
|
||||
|
||||
void eu07_application::on_focus_change(bool focus) {
|
||||
if( Global.bInactivePause && !m_network->client) {// jeśli ma być pauzowanie okna w tle
|
||||
command_relay relay;
|
||||
relay.post(user_command::focuspauseset, focus ? 1.0 : 0.0, 0.0, GLFW_PRESS, 0);
|
||||
}
|
||||
}
|
||||
|
||||
GLFWwindow *
|
||||
eu07_application::window(int const Windowindex, bool visible, int width, int height, GLFWmonitor *monitor, bool keep_ownership , bool share_ctx) {
|
||||
|
||||
@@ -373,7 +537,7 @@ eu07_application::window(int const Windowindex, bool visible, int width, int hei
|
||||
|
||||
// private:
|
||||
GLFWmonitor* eu07_application::find_monitor(const std::string &str) const {
|
||||
int monitor_count;
|
||||
int monitor_count;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&monitor_count);
|
||||
|
||||
for (size_t i = 0; i < monitor_count; i++) {
|
||||
@@ -394,17 +558,21 @@ std::string eu07_application::describe_monitor(GLFWmonitor *monitor) const {
|
||||
return name + ":" + std::to_string(x) + "," + std::to_string(y);
|
||||
}
|
||||
|
||||
// private:
|
||||
|
||||
void
|
||||
eu07_application::init_debug() {
|
||||
|
||||
#if defined(_MSC_VER) && defined (_DEBUG)
|
||||
// memory leaks
|
||||
_CrtSetDbgFlag( _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) | _CRTDBG_LEAK_CHECK_DF );
|
||||
/*
|
||||
// floating point operation errors
|
||||
auto state { _clearfp() };
|
||||
state = _control87( 0, 0 );
|
||||
// this will turn on FPE for #IND and zerodiv
|
||||
state = _control87( state & ~( _EM_ZERODIVIDE | _EM_INVALID ), _MCW_EM );
|
||||
*/
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
::SetUnhandledExceptionFilter( unhandled_handler );
|
||||
@@ -417,7 +585,11 @@ eu07_application::init_files() {
|
||||
#ifdef _WIN32
|
||||
DeleteFile( "log.txt" );
|
||||
DeleteFile( "errors.txt" );
|
||||
_mkdir( "logs" );
|
||||
CreateDirectory( "logs", NULL );
|
||||
#elif __unix__
|
||||
unlink("log.txt");
|
||||
unlink("errors.txt");
|
||||
mkdir("logs", 0755);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -425,10 +597,12 @@ int
|
||||
eu07_application::init_settings( int Argc, char *Argv[] ) {
|
||||
|
||||
Global.LoadIniFile( "eu07.ini" );
|
||||
#ifdef _WIN32
|
||||
if( ( Global.iWriteLogEnabled & 2 ) != 0 ) {
|
||||
// show output console if requested
|
||||
AllocConsole();
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
std::string executable( argv[ 0 ] ); auto const pathend = executable.rfind( '\\' );
|
||||
Global.ExecutableName =
|
||||
@@ -486,7 +660,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 {
|
||||
@@ -588,6 +762,7 @@ eu07_application::init_callbacks() {
|
||||
glfwSetMouseButtonCallback( window, mouse_button_callback );
|
||||
glfwSetKeyCallback( window, key_callback );
|
||||
glfwSetScrollCallback( window, scroll_callback );
|
||||
glfwSetCharCallback(window, char_callback);
|
||||
glfwSetWindowFocusCallback( window, focus_callback );
|
||||
{
|
||||
int width, height;
|
||||
@@ -668,7 +843,12 @@ eu07_application::init_data() {
|
||||
|
||||
int
|
||||
eu07_application::init_modes() {
|
||||
Global.local_random_engine.seed(std::random_device{}());
|
||||
|
||||
if ((!Global.network_servers.empty() || Global.network_client) && Global.SceneryFile.empty()) {
|
||||
ErrorLog("launcher mode is currently not supported in network mode");
|
||||
return -1;
|
||||
}
|
||||
// NOTE: we could delay creation/initialization until transition to specific mode is requested,
|
||||
// but doing it in one go at the start saves us some error checking headache down the road
|
||||
|
||||
@@ -687,3 +867,42 @@ eu07_application::init_modes() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool eu07_application::init_network() {
|
||||
if (!Global.network_servers.empty() || Global.network_client) {
|
||||
// create network manager
|
||||
m_network.emplace();
|
||||
}
|
||||
|
||||
for (auto const &pair : Global.network_servers) {
|
||||
// create all servers
|
||||
m_network->create_server(pair.first, pair.second);
|
||||
}
|
||||
|
||||
if (Global.network_client) {
|
||||
// create client
|
||||
m_network->connect(Global.network_client->first, Global.network_client->second);
|
||||
} else {
|
||||
// we're simulation master
|
||||
if (!Global.random_seed)
|
||||
Global.random_seed = std::random_device{}();
|
||||
Global.random_engine.seed(Global.random_seed);
|
||||
|
||||
// TODO: sort out this timezone mess
|
||||
std::time_t utc_now = std::time(nullptr);
|
||||
|
||||
tm tm_local, tm_utc;
|
||||
tm *tmp = std::localtime(&utc_now);
|
||||
memcpy(&tm_local, tmp, sizeof(tm));
|
||||
tmp = std::gmtime(&utc_now);
|
||||
memcpy(&tm_utc, tmp, sizeof(tm));
|
||||
|
||||
int64_t offset = (tm_local.tm_hour * 3600 + tm_local.tm_min * 60 + tm_local.tm_sec)
|
||||
- (tm_utc.tm_hour * 3600 + tm_utc.tm_min * 60 + tm_utc.tm_sec);
|
||||
|
||||
Global.starting_timestamp = utc_now + offset;
|
||||
Global.ready_to_load = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "applicationmode.h"
|
||||
#include "PyInt.h"
|
||||
#include "network/manager.h"
|
||||
|
||||
class eu07_application {
|
||||
const int MAX_NETWORK_PER_FRAME = 1000;
|
||||
|
||||
public:
|
||||
// types
|
||||
@@ -72,15 +74,24 @@ public:
|
||||
// input handlers
|
||||
void
|
||||
on_key( int const Key, int const Scancode, int const Action, int const Mods );
|
||||
void
|
||||
on_char( unsigned int const Char );
|
||||
void
|
||||
on_cursor_pos( double const Horizontal, double const Vertical );
|
||||
void
|
||||
on_mouse_button( int const Button, int const Action, int const Mods );
|
||||
void
|
||||
on_scroll( double const Xoffset, double const Yoffset );
|
||||
void
|
||||
on_focus_change(bool focus);
|
||||
// gives access to specified window, creates a new window if index == -1
|
||||
GLFWwindow *
|
||||
window( int const Windowindex = 0, bool visible = false, int width = 1, int height = 1, GLFWmonitor *monitor = nullptr, bool keep_ownership = true, bool share_ctx = true );
|
||||
// generate network sync verification number
|
||||
double
|
||||
generate_sync();
|
||||
void
|
||||
queue_quit();
|
||||
|
||||
private:
|
||||
// types
|
||||
@@ -97,6 +108,7 @@ private:
|
||||
int init_audio();
|
||||
int init_data();
|
||||
int init_modes();
|
||||
bool init_network();
|
||||
GLFWmonitor * find_monitor( const std::string &str ) const;
|
||||
std::string describe_monitor( GLFWmonitor *monitor ) const;
|
||||
// members
|
||||
@@ -104,6 +116,8 @@ private:
|
||||
mode_stack m_modestack; // current behaviour mode
|
||||
python_taskqueue m_taskqueue;
|
||||
std::vector<GLFWwindow *> m_windows;
|
||||
|
||||
std::optional<network::manager> m_network;
|
||||
};
|
||||
|
||||
extern eu07_application Application;
|
||||
|
||||
@@ -57,6 +57,9 @@ public:
|
||||
void
|
||||
on_key( int const Key, int const Scancode, int const Action, int const Mods ) = 0;
|
||||
virtual
|
||||
void
|
||||
on_char( unsigned int const Char ) = 0;
|
||||
virtual
|
||||
void
|
||||
on_cursor_pos( double const X, double const Y ) = 0;
|
||||
virtual
|
||||
@@ -68,6 +71,9 @@ public:
|
||||
virtual
|
||||
void
|
||||
on_event_poll() = 0;
|
||||
virtual
|
||||
bool
|
||||
is_command_processor() = 0;
|
||||
|
||||
protected:
|
||||
// members
|
||||
|
||||
128
command.cpp
128
command.cpp
@@ -14,6 +14,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "Logs.h"
|
||||
#include "Timer.h"
|
||||
#include "utilities.h"
|
||||
#include "simulation.h"
|
||||
#include "Train.h"
|
||||
|
||||
namespace simulation {
|
||||
|
||||
@@ -266,7 +268,36 @@ commanddescription_sequence Commands_descriptions = {
|
||||
{ "speedcontrolbutton6", command_target::vehicle },
|
||||
{ "speedcontrolbutton7", command_target::vehicle },
|
||||
{ "speedcontrolbutton8", command_target::vehicle },
|
||||
{ "speedcontrolbutton9", command_target::vehicle }
|
||||
{ "speedcontrolbutton9", command_target::vehicle },
|
||||
|
||||
{ "globalradiostop", command_target::simulation },
|
||||
{ "timejump", command_target::simulation },
|
||||
{ "timejumplarge", command_target::simulation },
|
||||
{ "timejumpsmall", command_target::simulation },
|
||||
{ "setdatetime", command_target::simulation },
|
||||
{ "setweather", command_target::simulation },
|
||||
{ "settemperature", command_target::simulation },
|
||||
{ "vehiclemoveforwards", command_target::vehicle },
|
||||
{ "vehiclemovebackwards", command_target::vehicle },
|
||||
{ "vehicleboost", command_target::vehicle },
|
||||
{ "debugtoggle", command_target::simulation },
|
||||
{ "focuspauseset", command_target::simulation },
|
||||
{ "pausetoggle", command_target::simulation },
|
||||
{ "entervehicle", command_target::simulation },
|
||||
{ "resetconsist", command_target::simulation },
|
||||
{ "fillcompressor", command_target::simulation },
|
||||
{ "consistreleaser", command_target::simulation },
|
||||
{ "queueevent", command_target::simulation },
|
||||
{ "setlight", command_target::simulation },
|
||||
{ "insertmodel", command_target::simulation },
|
||||
{ "deletemodel", command_target::simulation },
|
||||
{ "trainsetmove", command_target::simulation },
|
||||
{ "consistteleport", command_target::simulation },
|
||||
{ "pullalarmchain", command_target::simulation },
|
||||
{ "sendaicommand", command_target::simulation },
|
||||
{ "spawntrainset", command_target::simulation },
|
||||
{ "destroytrainset", command_target::simulation },
|
||||
{ "quitsimulation", command_target::simulation },
|
||||
};
|
||||
|
||||
} // simulation
|
||||
@@ -274,10 +305,18 @@ commanddescription_sequence Commands_descriptions = {
|
||||
// posts specified command for specified recipient
|
||||
void
|
||||
command_queue::push( command_data const &Command, std::size_t const Recipient ) {
|
||||
if (is_network_target(Recipient)) {
|
||||
auto lookup = m_intercept_queue.emplace(Recipient, commanddata_sequence());
|
||||
lookup.first->second.emplace_back(Command);
|
||||
} else {
|
||||
push_direct(Command, Recipient);
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
void command_queue::push_direct(const command_data &Command, const uint32_t Recipient) {
|
||||
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_back( Command );
|
||||
}
|
||||
|
||||
// retrieves oldest posted command for specified recipient, if any. returns: true on retrieval, false if there's nothing to retrieve
|
||||
@@ -296,15 +335,45 @@ 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;
|
||||
}
|
||||
|
||||
bool command_queue::is_network_target(uint32_t const Recipient) {
|
||||
const command_target target = (command_target)(Recipient & ~0xffff);
|
||||
|
||||
if (target == command_target::entity)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
command_queue::commands_map command_queue::pop_intercept_queue() {
|
||||
commands_map map(m_intercept_queue);
|
||||
m_intercept_queue.clear();
|
||||
return map;
|
||||
}
|
||||
|
||||
void command_queue::push_commands(const commands_map &commands) {
|
||||
for (auto const &kv : commands)
|
||||
for (command_data const &data : kv.second)
|
||||
push_direct(data, kv.first);
|
||||
}
|
||||
|
||||
void
|
||||
command_relay::post( user_command const Command, double const Param1, double const Param2, int const Action, std::uint16_t const Recipient ) const {
|
||||
command_relay::post(user_command const Command, double const Param1, double const Param2,
|
||||
int const Action, uint16_t Recipient, glm::vec3 Position, const std::string *Payload) 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 )
|
||||
@@ -313,44 +382,15 @@ command_relay::post( user_command const Command, double const Param1, double con
|
||||
return;
|
||||
}
|
||||
|
||||
simulation::Commands.push(
|
||||
command_data{
|
||||
Command,
|
||||
Action,
|
||||
Param1,
|
||||
Param2,
|
||||
Timer::GetDeltaTime() },
|
||||
static_cast<std::size_t>( command.target ) | Recipient );
|
||||
/*
|
||||
#ifdef _DEBUG
|
||||
if( Action != GLFW_RELEASE ) {
|
||||
// key was pressed or is still held
|
||||
if( false == command.name.empty() ) {
|
||||
if( false == (
|
||||
( Command == user_command::moveleft )
|
||||
|| ( Command == user_command::moveleftfast )
|
||||
|| ( Command == user_command::moveright )
|
||||
|| ( Command == user_command::moverightfast )
|
||||
|| ( Command == user_command::moveforward )
|
||||
|| ( Command == user_command::moveforwardfast )
|
||||
|| ( Command == user_command::moveback )
|
||||
|| ( Command == user_command::movebackfast )
|
||||
|| ( Command == user_command::moveup )
|
||||
|| ( Command == user_command::moveupfast )
|
||||
|| ( Command == user_command::movedown )
|
||||
|| ( Command == user_command::movedownfast )
|
||||
|| ( Command == user_command::movevector )
|
||||
|| ( Command == user_command::viewturn ) ) ) {
|
||||
WriteLog( "Command issued: " + command.name );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// key was released (but we don't log this)
|
||||
WriteLog( "Key released: " + command.name );
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
if (Position == glm::vec3(0.0f))
|
||||
Position = Global.pCamera.Pos;
|
||||
|
||||
uint32_t combined_recipient = static_cast<uint32_t>( command.target ) | Recipient;
|
||||
command_data commanddata({Command, Action, Param1, Param2, Timer::GetDeltaTime(), FreeFlyModeFlag, Position });
|
||||
if (Payload)
|
||||
commanddata.payload = *Payload;
|
||||
|
||||
simulation::Commands.push(commanddata, combined_recipient);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
75
command.h
75
command.h
@@ -260,13 +260,42 @@ enum class user_command {
|
||||
speedcontrolbutton7,
|
||||
speedcontrolbutton8,
|
||||
speedcontrolbutton9,
|
||||
|
||||
globalradiostop,
|
||||
timejump,
|
||||
timejumplarge,
|
||||
timejumpsmall,
|
||||
setdatetime,
|
||||
setweather,
|
||||
settemperature,
|
||||
vehiclemoveforwards,
|
||||
vehiclemovebackwards,
|
||||
vehicleboost,
|
||||
debugtoggle,
|
||||
focuspauseset,
|
||||
pausetoggle,
|
||||
entervehicle,
|
||||
resetconsist,
|
||||
fillcompressor,
|
||||
consistreleaser,
|
||||
queueevent,
|
||||
setlight,
|
||||
insertmodel,
|
||||
deletemodel,
|
||||
dynamicmove,
|
||||
consistteleport,
|
||||
pullalarmchain,
|
||||
sendaicommand,
|
||||
spawntrainset,
|
||||
destroytrainset,
|
||||
quitsimulation,
|
||||
|
||||
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
|
||||
@@ -276,7 +305,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 = 0x80000
|
||||
};
|
||||
|
||||
struct command_description {
|
||||
@@ -292,6 +323,11 @@ struct command_data {
|
||||
double param1;
|
||||
double param2;
|
||||
double time_delta;
|
||||
|
||||
bool freefly;
|
||||
glm::vec3 location;
|
||||
|
||||
std::string payload;
|
||||
};
|
||||
|
||||
// command_queues: collects and holds commands from input sources, for processing by their intended recipients
|
||||
@@ -301,6 +337,9 @@ struct command_data {
|
||||
class command_queue {
|
||||
|
||||
public:
|
||||
// types
|
||||
typedef std::deque<command_data> commanddata_sequence;
|
||||
typedef std::unordered_map<uint32_t, commanddata_sequence> commands_map;
|
||||
// methods
|
||||
// posts specified command for specified recipient
|
||||
void
|
||||
@@ -308,16 +347,36 @@ public:
|
||||
// retrieves oldest posted command for specified recipient, if any. returns: true on retrieval, false if there's nothing to retrieve
|
||||
bool
|
||||
pop( command_data &Command, std::size_t const Recipient );
|
||||
// checks if given command must be scheduled on server
|
||||
bool
|
||||
is_network_target(const uint32_t Recipient);
|
||||
|
||||
// pops commands from intercept queue
|
||||
commands_map pop_intercept_queue();
|
||||
|
||||
// pushes commands into main queue
|
||||
void push_commands(const commands_map &commands);
|
||||
|
||||
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;
|
||||
// contains command ready to execution
|
||||
commands_map m_commands;
|
||||
|
||||
// contains intercepted commands to be read by application layer
|
||||
commands_map m_intercept_queue;
|
||||
|
||||
void push_direct( command_data const &Command, uint32_t const Recipient );
|
||||
};
|
||||
|
||||
template<typename A, typename B>
|
||||
void add_to_dequemap(std::unordered_map<A, std::deque<B>> &lhs, const std::unordered_map<A, std::deque<B>> &rhs) {
|
||||
for (auto const &kv : rhs) {
|
||||
auto lookup = lhs.emplace(kv.first, std::deque<B>());
|
||||
for (B const &data : kv.second)
|
||||
lookup.first->second.emplace_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: simulation should be a (light) wrapper rather than namespace so we could potentially instance it,
|
||||
// but realistically it's not like we're going to run more than one simulation at a time
|
||||
namespace simulation {
|
||||
@@ -340,8 +399,8 @@ public:
|
||||
// posts specified command for the specified recipient
|
||||
// TODO: replace uint16_t with recipient handle, based on item id
|
||||
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, glm::vec3 Position = glm::vec3(0.0f) , const std::string *Payload = nullptr) const;
|
||||
private:
|
||||
// types
|
||||
// members
|
||||
|
||||
@@ -244,7 +244,7 @@ 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 },
|
||||
// coolingfanstoggle,
|
||||
// tempomattoggle,
|
||||
// springbraketoggle,
|
||||
@@ -269,6 +269,15 @@ driverkeyboard_input::default_bindings() {
|
||||
// speedcontrolbutton7,
|
||||
// speedcontrolbutton8,
|
||||
// speedcontrolbutton9,
|
||||
// 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 },
|
||||
{ user_command::debugtoggle, GLFW_KEY_F12 | keymodifier::control | keymodifier::shift },
|
||||
{ user_command::pausetoggle, GLFW_KEY_ESCAPE }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
178
drivermode.cpp
178
drivermode.cpp
@@ -226,11 +226,26 @@ driver_mode::update() {
|
||||
|
||||
// variable step simulation time routines
|
||||
|
||||
if (!change_train.empty()) {
|
||||
TTrain *train = simulation::Trains.find(change_train);
|
||||
if (train) {
|
||||
simulation::Train = train;
|
||||
InOutKey();
|
||||
m_relay.post(user_command::aidriverdisable, 0.0, 0.0, GLFW_PRESS, 0);
|
||||
change_train.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if( ( simulation::Train == nullptr ) && ( false == FreeFlyModeFlag ) ) {
|
||||
// intercept cases when the driven train got removed after entering portal
|
||||
InOutKey();
|
||||
}
|
||||
|
||||
if (!FreeFlyModeFlag && simulation::Train->Dynamic() != Camera.m_owner) {
|
||||
// fixup camera after vehicle switch
|
||||
CabView();
|
||||
}
|
||||
/*
|
||||
if( Global.changeDynObj ) {
|
||||
// ABu zmiana pojazdu - przejście do innego
|
||||
ChangeDynamic();
|
||||
@@ -243,15 +258,13 @@ driver_mode::update() {
|
||||
InOutKey();
|
||||
}
|
||||
}
|
||||
*/
|
||||
if( simulation::Train != nullptr )
|
||||
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( simulation::Train->Dynamic() );
|
||||
else
|
||||
TSubModel::iInstance = 0;
|
||||
|
||||
if( simulation::Train != nullptr ) {
|
||||
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( simulation::Train->Dynamic() );
|
||||
simulation::Train->Update( deltatime );
|
||||
}
|
||||
else {
|
||||
TSubModel::iInstance = 0;
|
||||
}
|
||||
|
||||
simulation::Trains.update(deltatime);
|
||||
simulation::Events.update();
|
||||
simulation::Region->update_events();
|
||||
simulation::Lights.update();
|
||||
@@ -260,12 +273,16 @@ driver_mode::update() {
|
||||
|
||||
auto const deltarealtime = Timer::GetDeltaRenderTime(); // nie uwzględnia pauzowania ani mnożenia czasu
|
||||
|
||||
simulation::State.process_commands();
|
||||
|
||||
// fixed step render time routines
|
||||
|
||||
fTime50Hz += deltarealtime; // w pauzie też trzeba zliczać czas, bo przy dużym FPS będzie problem z odczytem ramek
|
||||
bool runonce { false };
|
||||
while( fTime50Hz >= 1.0 / 50.0 ) {
|
||||
#ifdef _WIN32
|
||||
Console::Update(); // to i tak trzeba wywoływać
|
||||
#endif
|
||||
ui::Transcripts.Update(); // obiekt obsługujący stenogramy dźwięków na ekranie
|
||||
m_userinterface->update();
|
||||
// decelerate camera
|
||||
@@ -354,8 +371,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], nPlayerTrain );
|
||||
@@ -364,32 +381,17 @@ driver_mode::enter() {
|
||||
|
||||
if (nPlayerTrain)
|
||||
{
|
||||
WriteLog( "Initializing player train, \"" + Global.asHumanCtrlVehicle + "\"" );
|
||||
WriteLog( "Trying to enter player train, \"" + Global.local_start_vehicle + "\"" );
|
||||
|
||||
if( simulation::Train == nullptr ) {
|
||||
simulation::Train = new TTrain();
|
||||
}
|
||||
if( simulation::Train->Init( nPlayerTrain ) )
|
||||
{
|
||||
WriteLog("Player train initialization OK");
|
||||
|
||||
Application.set_title( Global.AppName + " (" + simulation::Train->Controlled()->Name + " @ " + Global.SceneryFile + ")" );
|
||||
|
||||
CabView();
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("Bad init: player train initialization failed");
|
||||
FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie
|
||||
SafeDelete( simulation::Train );
|
||||
Camera.m_owner = nullptr;
|
||||
}
|
||||
FreeFlyModeFlag = true; // HACK: entervehicle won't work if the simulation thinks we're outside
|
||||
m_relay.post(user_command::entervehicle, 0.0, 0.0, GLFW_PRESS, 0, nPlayerTrain->GetPosition());
|
||||
change_train = nPlayerTrain->name();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Global.asHumanCtrlVehicle != "ghostview")
|
||||
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 + "\"" );
|
||||
}
|
||||
FreeFlyModeFlag = true; // Ra: automatycznie włączone latanie
|
||||
Camera.m_owner = nullptr;
|
||||
@@ -459,6 +461,11 @@ driver_mode::on_key( int const Key, int const Scancode, int const Action, int co
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
driver_mode::on_char( unsigned int const Char ) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void
|
||||
driver_mode::on_cursor_pos( double const Horizontal, double const Vertical ) {
|
||||
|
||||
@@ -498,6 +505,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 ) {
|
||||
|
||||
@@ -759,7 +772,7 @@ driver_mode::OnKeyDown(int cKey) {
|
||||
// z [Shift] uruchomienie eventu
|
||||
if( ( Global.iPause == 0 ) // podczas pauzy klawisze nie działają
|
||||
&& ( KeyEvents[ i ] != nullptr ) ) {
|
||||
simulation::Events.AddToQuery( KeyEvents[ i ], NULL );
|
||||
m_relay.post(user_command::queueevent, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &KeyEvents[i]->name());
|
||||
}
|
||||
}
|
||||
else if( Global.ctrlState ) {
|
||||
@@ -794,21 +807,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
|
||||
@@ -817,6 +815,19 @@ driver_mode::OnKeyDown(int cKey) {
|
||||
}
|
||||
case GLFW_KEY_F5: {
|
||||
// przesiadka do innego pojazdu
|
||||
if (!FreeFlyModeFlag)
|
||||
// only available in free fly mode
|
||||
break;
|
||||
|
||||
TDynamicObject *dynamic = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global.pCamera.Pos, 50, false, false ) );
|
||||
if (dynamic) {
|
||||
m_relay.post(user_command::entervehicle, 0.0, 0.0, GLFW_PRESS, 0);
|
||||
|
||||
change_train = dynamic->name();
|
||||
}
|
||||
|
||||
break;
|
||||
/*
|
||||
if( false == FreeFlyModeFlag ) {
|
||||
// only available in free fly mode
|
||||
break;
|
||||
@@ -872,12 +883,10 @@ driver_mode::OnKeyDown(int cKey) {
|
||||
// we can simply move the 'human' controller to the new vehicle
|
||||
Global.changeDynObj = targetvehicle;
|
||||
// TODO: choose active cab based on camera's location relative to vehicle's location
|
||||
/*
|
||||
Global.changeDynObj->MoverParameters->CabOccupied = (
|
||||
Train->DynamicObject->MoverParameters->Neighbours[ exitdirection ].vehicle_end ?
|
||||
-1 :
|
||||
1 );
|
||||
*/
|
||||
// Global.changeDynObj->MoverParameters->CabOccupied = (
|
||||
// Train->DynamicObject->MoverParameters->Neighbours[ exitdirection ].vehicle_end ?
|
||||
// -1 :
|
||||
// 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -905,6 +914,7 @@ driver_mode::OnKeyDown(int cKey) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
}
|
||||
case GLFW_KEY_F6: {
|
||||
// przyspieszenie symulacji do testowania scenerii... uwaga na FPS!
|
||||
@@ -954,66 +964,6 @@ driver_mode::OnKeyDown(int cKey) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GLFW_KEY_F12: {
|
||||
// quick debug mode toggle
|
||||
if( Global.ctrlState
|
||||
&& Global.shiftState ) {
|
||||
DebugModeFlag = !DebugModeFlag;
|
||||
}
|
||||
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;
|
||||
@@ -1267,7 +1217,7 @@ driver_mode::ChangeDynamic() {
|
||||
vehicle = train->Dynamic();
|
||||
occupied = train->Occupied();
|
||||
driver = vehicle->Mechanik;
|
||||
Global.asHumanCtrlVehicle = vehicle->name();
|
||||
Global.local_start_vehicle = vehicle->name();
|
||||
if( driver ) // AI może sobie samo pójść
|
||||
if( false == driver->AIControllFlag ) // tylko jeśli ręcznie prowadzony
|
||||
{
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
// input handlers
|
||||
void
|
||||
on_key( int const Key, int const Scancode, int const Action, int const Mods ) override;
|
||||
void
|
||||
on_char( unsigned int const Char ) override;
|
||||
void
|
||||
on_cursor_pos( double const Horizontal, double const Vertical ) override;
|
||||
void
|
||||
@@ -49,6 +51,8 @@ public:
|
||||
on_scroll( double const Xoffset, double const Yoffset ) override;
|
||||
void
|
||||
on_event_poll() override;
|
||||
bool
|
||||
is_command_processor() override;
|
||||
|
||||
private:
|
||||
// types
|
||||
@@ -109,4 +113,6 @@ private:
|
||||
double m_primaryupdateaccumulator { m_secondaryupdaterate }; // keeps track of elapsed simulation time, for core fixed step routines
|
||||
double m_secondaryupdateaccumulator { m_secondaryupdaterate }; // keeps track of elapsed simulation time, for less important fixed step routines
|
||||
int iPause { 0 }; // wykrywanie zmian w zapauzowaniu
|
||||
command_relay m_relay;
|
||||
std::string change_train; // train name awaiting entering
|
||||
};
|
||||
|
||||
@@ -47,22 +47,6 @@ driver_ui::driver_ui() {
|
||||
bool
|
||||
driver_ui::on_key_( int const Key, int const Scancode, int const Action, int const Mods ) {
|
||||
|
||||
if( Key == GLFW_KEY_ESCAPE ) {
|
||||
// toggle pause
|
||||
if( Action != GLFW_PRESS ) { return true; } // recognized, but ignored
|
||||
|
||||
if( Global.iPause & 1 ) {
|
||||
// jeśli pauza startowa
|
||||
// odpauzowanie, gdy po wczytaniu miało nie startować
|
||||
Global.iPause ^= 1;
|
||||
}
|
||||
else if( ( Global.iMultiplayer & 2 ) == 0 ) {
|
||||
// w multiplayerze pauza nie ma sensu
|
||||
Global.iPause ^= 2; // zmiana stanu zapauzowania
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the pause is on ignore block other input
|
||||
if( m_paused ) { return true; }
|
||||
|
||||
@@ -192,12 +176,13 @@ driver_ui::render_() {
|
||||
auto const popupwidth{ locale::strings[ locale::string::driver_pause_header ].size() * 7 };
|
||||
if( ImGui::Button( locale::strings[ locale::string::driver_pause_resume ].c_str(), ImVec2( popupwidth, 0 ) ) ) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
auto const pausemask { 1 | 2 };
|
||||
Global.iPause &= ~pausemask;
|
||||
command_relay commandrelay;
|
||||
commandrelay.post(user_command::pausetoggle, 0.0, 0.0, GLFW_PRESS, 0);
|
||||
}
|
||||
if( ImGui::Button( locale::strings[ locale::string::driver_pause_quit ].c_str(), ImVec2( popupwidth, 0 ) ) ) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
glfwSetWindowShouldClose( m_window, 1 );
|
||||
command_relay commandrelay;
|
||||
commandrelay.post(user_command::quitsimulation, 0.0, 0.0, GLFW_PRESS, 0);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
@@ -181,6 +181,11 @@ editor_mode::on_key( int const Key, int const Scancode, int const Action, int co
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
editor_mode::on_char( unsigned int const Char ) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void
|
||||
editor_mode::on_cursor_pos( double const Horizontal, double const Vertical ) {
|
||||
|
||||
@@ -266,6 +271,12 @@ editor_mode::on_event_poll() {
|
||||
m_input.poll();
|
||||
}
|
||||
|
||||
bool
|
||||
editor_mode::is_command_processor() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
editor_mode::mode_translation() const {
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
// input handlers
|
||||
void
|
||||
on_key( int const Key, int const Scancode, int const Action, int const Mods ) override;
|
||||
void
|
||||
on_char( unsigned int const Char ) override;
|
||||
void
|
||||
on_cursor_pos( double const Horizontal, double const Vertical ) override;
|
||||
void
|
||||
@@ -45,6 +47,8 @@ public:
|
||||
on_scroll( double const Xoffset, double const Yoffset ) override;
|
||||
void
|
||||
on_event_poll() override;
|
||||
bool
|
||||
is_command_processor() override;
|
||||
|
||||
private:
|
||||
// types
|
||||
|
||||
@@ -84,10 +84,10 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;WIN32_LEAN_AND_MEAN;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/asio/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@@ -108,10 +108,10 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;WIN32_LEAN_AND_MEAN;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/asio/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@@ -128,10 +128,10 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;WIN32_LEAN_AND_MEAN;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/asio/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
@@ -151,10 +151,10 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;WIN32_LEAN_AND_MEAN;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glew/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)console;$(SolutionDir)mczapkie;$(SolutionDir)ref/glad/include;$(SolutionDir)ref/glfw/include;$(SolutionDir)ref/glm;$(SolutionDir)ref/openal/include;$(SolutionDir)ref/dr_libs/include;$(SolutionDir)ref/imgui;$(SolutionDir)ref/imgui/examples;$(SolutionDir)ref/python/include;$(SolutionDir)ref/libserialport/include;$(SolutionDir)ref/asio/include;$(SolutionDir)ref/stb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
@@ -234,6 +234,10 @@
|
||||
<ClCompile Include="drivermouseinput.cpp" />
|
||||
<ClCompile Include="mtable.cpp" />
|
||||
<ClCompile Include="Names.cpp" />
|
||||
<ClCompile Include="network\backend\asio.cpp" />
|
||||
<ClCompile Include="network\manager.cpp" />
|
||||
<ClCompile Include="network\message.cpp" />
|
||||
<ClCompile Include="network\network.cpp" />
|
||||
<ClCompile Include="opengl33geometrybank.cpp" />
|
||||
<ClCompile Include="opengl33light.cpp" />
|
||||
<ClCompile Include="opengl33particles.cpp" />
|
||||
@@ -409,6 +413,10 @@
|
||||
<ClInclude Include="drivermouseinput.h" />
|
||||
<ClInclude Include="mtable.h" />
|
||||
<ClInclude Include="Names.h" />
|
||||
<ClInclude Include="network\backend\asio.h" />
|
||||
<ClInclude Include="network\manager.h" />
|
||||
<ClInclude Include="network\message.h" />
|
||||
<ClInclude Include="network\network.h" />
|
||||
<ClInclude Include="opengl33geometrybank.h" />
|
||||
<ClInclude Include="opengl33light.h" />
|
||||
<ClInclude Include="opengl33particles.h" />
|
||||
|
||||
@@ -70,6 +70,18 @@
|
||||
<Filter Include="Source Files\math">
|
||||
<UniqueIdentifier>{dd00198e-a316-4bcc-a4d3-916c8dcfe08f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\application\network">
|
||||
<UniqueIdentifier>{a41f784b-da5a-40de-a4ec-f9b1da1aa607}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\application\network">
|
||||
<UniqueIdentifier>{5a6c58fe-4b3c-4a46-8ef0-71d4c08d7ebe}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\application\network\backend">
|
||||
<UniqueIdentifier>{de8b7596-9589-4320-9008-15e15ebf1d8f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\application\network\backend">
|
||||
<UniqueIdentifier>{db0ac2d6-9a5d-412e-87b6-8b23c8f8b7a7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AirCoupler.cpp">
|
||||
@@ -441,6 +453,18 @@
|
||||
<ClCompile Include="opengl33geometrybank.cpp">
|
||||
<Filter>Source Files\gfx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="network\network.cpp">
|
||||
<Filter>Source Files\application\network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="network\manager.cpp">
|
||||
<Filter>Source Files\application\network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="network\message.cpp">
|
||||
<Filter>Source Files\application\network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="network\backend\asio.cpp">
|
||||
<Filter>Source Files\application\network\backend</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Globals.h">
|
||||
@@ -812,6 +836,18 @@
|
||||
<ClInclude Include="stars.h">
|
||||
<Filter>Header Files\gfx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="network\manager.h">
|
||||
<Filter>Header Files\application\network</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="network\message.h">
|
||||
<Filter>Header Files\application\network</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="network\network.h">
|
||||
<Filter>Header Files\application\network</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="network\backend\asio.h">
|
||||
<Filter>Header Files\application\network\backend</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="maszyna.rc">
|
||||
|
||||
@@ -68,7 +68,8 @@ OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
|
||||
|| ( typeid( *event ) == typeid( lights_event ) )
|
||||
|| ( event->m_sibling != 0 ) ) {
|
||||
// tylko jawne albo niejawne Multiple
|
||||
simulation::Events.AddToQuery( event, nullptr ); // drugi parametr to dynamic wywołujący - tu brak
|
||||
command_relay relay;
|
||||
relay.post(user_command::queueevent, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &event->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,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::Vehicles.find( Global.local_start_vehicle ) :
|
||||
simulation::Vehicles.find( std::string{ pRozkaz->cString + 1, (unsigned)pRozkaz->cString[ 0 ] } ) );
|
||||
if( vehicle != nullptr ) {
|
||||
WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe
|
||||
@@ -180,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::Vehicles.find( Global.local_start_vehicle ) : // 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 };
|
||||
|
||||
244
network/backend/asio.cpp
Normal file
244
network/backend/asio.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "stdafx.h"
|
||||
#include "network/backend/asio.h"
|
||||
|
||||
#include "sn_utils.h"
|
||||
#include "Logs.h"
|
||||
|
||||
network::tcp::connection::connection(asio::io_context &io_ctx, bool client, size_t counter)
|
||||
: network::connection(client, counter),
|
||||
m_socket(io_ctx)
|
||||
{
|
||||
m_header_buffer.resize(8);
|
||||
}
|
||||
|
||||
network::tcp::connection::~connection()
|
||||
{
|
||||
m_socket.shutdown(m_socket.shutdown_both);
|
||||
m_socket.close();
|
||||
}
|
||||
|
||||
void network::tcp::connection::disconnect()
|
||||
{
|
||||
network::connection::disconnect();
|
||||
}
|
||||
|
||||
void network::tcp::connection::send_data(std::shared_ptr<std::string> buffer)
|
||||
{
|
||||
asio::async_write(m_socket, asio::buffer(*buffer.get()), std::bind(&connection::send_complete, this, buffer));
|
||||
}
|
||||
|
||||
void network::tcp::connection::connected()
|
||||
{
|
||||
network::connection::connected();
|
||||
read_header();
|
||||
}
|
||||
|
||||
void network::tcp::connection::read_header()
|
||||
{
|
||||
asio::async_read(m_socket, asio::buffer(m_header_buffer),
|
||||
std::bind(&connection::handle_header, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void network::tcp::connection::handle_header(const asio::error_code &err, size_t bytes_transferred)
|
||||
{
|
||||
if (err) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream header(m_header_buffer);
|
||||
if (m_header_buffer.size() != bytes_transferred) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t sig = sn_utils::ld_uint32(header);
|
||||
if (sig != NETWORK_MAGIC) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t len = sn_utils::ld_uint32(header);
|
||||
m_body_buffer.resize(len);
|
||||
if (len > MAX_MSG_SIZE) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
asio::async_read(m_socket, asio::buffer(m_body_buffer),
|
||||
std::bind(&connection::handle_data, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void network::tcp::connection::handle_data(const asio::error_code &err, size_t bytes_transferred)
|
||||
{
|
||||
if (err) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_body_buffer.size() != bytes_transferred) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream stream(m_body_buffer);
|
||||
std::shared_ptr<message> msg = deserialize_message(stream);
|
||||
if (message_handler)
|
||||
message_handler(*msg);
|
||||
|
||||
read_header();
|
||||
}
|
||||
|
||||
void network::tcp::connection::write_message(const message &msg, std::ostream &stream)
|
||||
{
|
||||
size_t beg = (size_t)stream.tellp();
|
||||
|
||||
sn_utils::ls_uint32(stream, NETWORK_MAGIC);
|
||||
sn_utils::ls_uint32(stream, 0);
|
||||
|
||||
serialize_message(msg, stream);
|
||||
|
||||
size_t size = (size_t)stream.tellp() - beg - 8;
|
||||
if (size > MAX_MSG_SIZE) {
|
||||
ErrorLog("net: message too big", logtype::net);
|
||||
return;
|
||||
}
|
||||
stream.seekp(beg + 4, std::ios_base::beg);
|
||||
sn_utils::ls_uint32(stream, size);
|
||||
|
||||
stream.seekp(0, std::ios_base::end);
|
||||
}
|
||||
|
||||
void network::tcp::connection::send_messages(const std::vector<std::shared_ptr<message> > &messages)
|
||||
{
|
||||
if (messages.size() == 0)
|
||||
return;
|
||||
|
||||
std::ostringstream stream;
|
||||
for (auto const &msg : messages)
|
||||
write_message(*msg.get(), stream);
|
||||
|
||||
stream.flush();
|
||||
send_data(std::make_shared<std::string>(stream.str()));
|
||||
}
|
||||
|
||||
void network::tcp::connection::send_message(const message &msg)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
write_message(msg, stream);
|
||||
|
||||
stream.flush();
|
||||
send_data(std::make_shared<std::string>(stream.str()));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
network::tcp::server::server(std::shared_ptr<std::istream> buf, asio::io_context &io_ctx, const std::string &host, uint32_t port)
|
||||
: network::server(buf), m_acceptor(io_ctx)
|
||||
{
|
||||
auto endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(host), port);
|
||||
m_acceptor.open(endpoint.protocol());
|
||||
m_acceptor.set_option(asio::socket_base::reuse_address(true));
|
||||
m_acceptor.set_option(asio::ip::tcp::no_delay(true));
|
||||
m_acceptor.bind(endpoint);
|
||||
m_acceptor.listen(10);
|
||||
|
||||
accept_conn();
|
||||
}
|
||||
|
||||
void network::tcp::server::accept_conn()
|
||||
{
|
||||
std::shared_ptr<connection> conn = std::make_shared<connection>(m_acceptor.get_executor().context());
|
||||
conn->set_handler(std::bind(&server::handle_message, this, conn, std::placeholders::_1));
|
||||
|
||||
m_acceptor.async_accept(conn->m_socket, std::bind(&server::handle_accept, this, conn, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void network::tcp::server::handle_accept(std::shared_ptr<connection> conn, const asio::error_code &err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
clients.emplace_back(conn);
|
||||
conn->connected();
|
||||
}
|
||||
else
|
||||
{
|
||||
conn->state = connection::DEAD;
|
||||
WriteLog(std::string("net: failed to accept client: " + err.message()), logtype::net);
|
||||
}
|
||||
|
||||
accept_conn();
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
network::tcp::client::client(asio::io_context &io_ctxx, const std::string &hostarg, uint32_t portarg)
|
||||
: host(hostarg), port(portarg), io_ctx(io_ctxx)
|
||||
{
|
||||
}
|
||||
|
||||
void network::tcp::client::connect()
|
||||
{
|
||||
if (this->conn)
|
||||
return;
|
||||
|
||||
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));
|
||||
|
||||
asio::ip::tcp::endpoint endpoint(
|
||||
asio::ip::address::from_string(host), port);
|
||||
conn->m_socket.open(endpoint.protocol());
|
||||
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));
|
||||
|
||||
this->conn = conn;
|
||||
|
||||
}
|
||||
|
||||
void network::tcp::client::handle_accept(const asio::error_code &err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
conn->connected();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog(std::string("net: failed to connect: " + err.message()), logtype::net);
|
||||
conn->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
network::tcp::asio_manager::asio_manager() {
|
||||
backend_list().emplace("tcp", this);
|
||||
}
|
||||
|
||||
std::shared_ptr<network::server> network::tcp::asio_manager::create_server(std::shared_ptr<std::fstream> backbuffer, const std::string &conf) {
|
||||
std::istringstream stream(conf);
|
||||
|
||||
std::string host;
|
||||
std::getline(stream, host, ':');
|
||||
|
||||
int port;
|
||||
stream >> port;
|
||||
|
||||
return std::make_shared<tcp::server>(backbuffer, io_context, host, port);
|
||||
}
|
||||
|
||||
std::shared_ptr<network::client> network::tcp::asio_manager::create_client(const std::string &conf) {
|
||||
std::istringstream stream(conf);
|
||||
|
||||
std::string host;
|
||||
std::getline(stream, host, ':');
|
||||
|
||||
int port;
|
||||
stream >> port;
|
||||
|
||||
return std::make_shared<tcp::client>(io_context, host, port);
|
||||
}
|
||||
|
||||
void network::tcp::asio_manager::update() {
|
||||
io_context.restart();
|
||||
io_context.poll();
|
||||
}
|
||||
82
network/backend/asio.h
Normal file
82
network/backend/asio.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#define ASIO_STANDALONE
|
||||
|
||||
#ifdef DBG_NEW
|
||||
#undef new
|
||||
#endif
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include "network/network.h"
|
||||
|
||||
namespace network::tcp
|
||||
{
|
||||
const uint32_t NETWORK_MAGIC = 0x37305545;
|
||||
const uint32_t MAX_MSG_SIZE = 100000;
|
||||
|
||||
class connection : public network::connection
|
||||
{
|
||||
friend class server;
|
||||
friend class client;
|
||||
|
||||
public:
|
||||
connection(asio::io_context &io_ctx, bool client = false, size_t counter = 0);
|
||||
~connection();
|
||||
|
||||
virtual void connected() override;
|
||||
virtual void disconnect() override;
|
||||
virtual void send_messages(const std::vector<std::shared_ptr<message> > &messages) override;
|
||||
virtual void send_message(const message &msg) override;
|
||||
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
private:
|
||||
std::string m_header_buffer;
|
||||
std::string m_body_buffer;
|
||||
|
||||
void write_message(const message &msg, std::ostream &stream);
|
||||
void send_data(std::shared_ptr<std::string> buffer);
|
||||
void read_header();
|
||||
void handle_header(const asio::error_code &err, size_t bytes_transferred);
|
||||
void handle_data(const asio::error_code &err, size_t bytes_transferred);
|
||||
};
|
||||
|
||||
class server : public network::server
|
||||
{
|
||||
private:
|
||||
void accept_conn();
|
||||
void handle_accept(std::shared_ptr<connection> conn, const asio::error_code &err);
|
||||
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
|
||||
public:
|
||||
server(std::shared_ptr<std::istream> buf, asio::io_context &io_ctx, const std::string &host, uint32_t port);
|
||||
};
|
||||
|
||||
class client : public network::client
|
||||
{
|
||||
private:
|
||||
void handle_accept(const asio::error_code &err);
|
||||
asio::io_context &io_ctx;
|
||||
std::string host;
|
||||
uint32_t port;
|
||||
|
||||
protected:
|
||||
virtual void connect() override;
|
||||
|
||||
public:
|
||||
client(asio::io_context &io_ctx, const std::string &host, uint32_t port);
|
||||
};
|
||||
|
||||
class asio_manager : public network::backend_manager {
|
||||
asio::io_context io_context;
|
||||
|
||||
public:
|
||||
asio_manager();
|
||||
|
||||
virtual std::shared_ptr<network::server> create_server(std::shared_ptr<std::fstream>, const std::string &conf) override;
|
||||
virtual std::shared_ptr<network::client> create_client(const std::string &conf) override;
|
||||
virtual void update() override;
|
||||
};
|
||||
|
||||
asio_manager manager;
|
||||
}
|
||||
89
network/manager.cpp
Normal file
89
network/manager.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "network/manager.h"
|
||||
|
||||
#include "simulation.h"
|
||||
#include "Logs.h"
|
||||
|
||||
network::server_manager::server_manager()
|
||||
{
|
||||
backbuffer = std::make_shared<std::fstream>("backbuffer.bin", std::ios::out | std::ios::in | std::ios::trunc | std::ios::binary);
|
||||
}
|
||||
|
||||
command_queue::commands_map network::server_manager::pop_commands()
|
||||
{
|
||||
command_queue::commands_map map;
|
||||
|
||||
for (auto srv : servers)
|
||||
add_to_dequemap(map, srv->pop_commands());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void network::server_manager::push_delta(double render_dt, double dt, double sync, const command_queue::commands_map &commands)
|
||||
{
|
||||
if (dt == 0.0 && commands.empty())
|
||||
return;
|
||||
|
||||
frame_info msg;
|
||||
msg.render_dt = render_dt;
|
||||
msg.dt = dt;
|
||||
msg.sync = sync;
|
||||
msg.commands = commands;
|
||||
|
||||
for (auto srv : servers)
|
||||
srv->push_delta(msg);
|
||||
|
||||
serialize_message(msg, *backbuffer.get());
|
||||
}
|
||||
|
||||
void network::server_manager::create_server(const std::string &backend, const std::string &conf)
|
||||
{
|
||||
auto it = backend_list().find(backend);
|
||||
if (it == backend_list().end()) {
|
||||
ErrorLog("net: unknown backend: " + backend);
|
||||
return;
|
||||
}
|
||||
|
||||
servers.emplace_back(it->second->create_server(backbuffer, conf));
|
||||
}
|
||||
|
||||
network::manager::manager()
|
||||
{
|
||||
}
|
||||
|
||||
void network::manager::update()
|
||||
{
|
||||
for (auto &backend : backend_list())
|
||||
backend.second->update();
|
||||
|
||||
if (client)
|
||||
client->update();
|
||||
}
|
||||
|
||||
void network::manager::create_server(const std::string &backend, const std::string &conf)
|
||||
{
|
||||
servers.emplace();
|
||||
servers->create_server(backend, conf);
|
||||
}
|
||||
|
||||
void network::manager::connect(const std::string &backend, const std::string &conf)
|
||||
{
|
||||
auto it = backend_list().find(backend);
|
||||
if (it == backend_list().end()) {
|
||||
ErrorLog("net: unknown backend: " + backend);
|
||||
return;
|
||||
}
|
||||
|
||||
client = it->second->create_client(conf);
|
||||
}
|
||||
|
||||
//std::unordered_map<std::string, std::shared_ptr<network::backend_manager>> network::backend_list;
|
||||
34
network/manager.h
Normal file
34
network/manager.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "network/network.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace network
|
||||
{
|
||||
class server_manager
|
||||
{
|
||||
private:
|
||||
std::vector<std::shared_ptr<server>> servers;
|
||||
std::shared_ptr<std::fstream> backbuffer;
|
||||
|
||||
public:
|
||||
server_manager();
|
||||
|
||||
void push_delta(double render_dt, double dt, double sync, const command_queue::commands_map &commands);
|
||||
command_queue::commands_map pop_commands();
|
||||
void create_server(const std::string &backend, const std::string &conf);
|
||||
};
|
||||
|
||||
class manager
|
||||
{
|
||||
public:
|
||||
manager();
|
||||
|
||||
std::optional<server_manager> servers;
|
||||
std::shared_ptr<network::client> client;
|
||||
|
||||
void create_server(const std::string &backend, const std::string &conf);
|
||||
void connect(const std::string &backend, const std::string &conf);
|
||||
void update();
|
||||
};
|
||||
}
|
||||
124
network/message.cpp
Normal file
124
network/message.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "stdafx.h"
|
||||
#include "network/message.h"
|
||||
#include "sn_utils.h"
|
||||
|
||||
void network::client_hello::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_int32(stream, version);
|
||||
sn_utils::ls_uint32(stream, start_packet);
|
||||
}
|
||||
|
||||
void network::client_hello::deserialize(std::istream &stream)
|
||||
{
|
||||
version = sn_utils::ld_int32(stream);
|
||||
start_packet = sn_utils::ld_uint32(stream);
|
||||
}
|
||||
|
||||
void network::server_hello::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_uint32(stream, seed);
|
||||
sn_utils::ls_int64(stream, timestamp);
|
||||
}
|
||||
|
||||
void network::server_hello::deserialize(std::istream &stream)
|
||||
{
|
||||
seed = sn_utils::ld_uint32(stream);
|
||||
timestamp = sn_utils::ld_int64(stream);
|
||||
}
|
||||
|
||||
void ::network::request_command::serialize(std::ostream &stream) const
|
||||
{
|
||||
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);
|
||||
|
||||
sn_utils::s_bool(stream, data.freefly);
|
||||
sn_utils::s_vec3(stream, data.location);
|
||||
|
||||
sn_utils::s_str(stream, data.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void network::request_command::deserialize(std::istream &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);
|
||||
|
||||
data.freefly = sn_utils::d_bool(stream);
|
||||
data.location = sn_utils::d_vec3(stream);
|
||||
|
||||
data.payload = sn_utils::d_str(stream);
|
||||
|
||||
sequence.emplace_back(data);
|
||||
}
|
||||
|
||||
commands.emplace(recipient, sequence);
|
||||
}
|
||||
}
|
||||
|
||||
void network::frame_info::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_float64(stream, render_dt);
|
||||
sn_utils::ls_float64(stream, dt);
|
||||
sn_utils::ls_float64(stream, sync);
|
||||
|
||||
request_command::serialize(stream);
|
||||
}
|
||||
|
||||
void network::frame_info::deserialize(std::istream &stream)
|
||||
{
|
||||
render_dt = sn_utils::ld_float64(stream);
|
||||
dt = sn_utils::ld_float64(stream);
|
||||
sync = sn_utils::ld_float64(stream);
|
||||
|
||||
request_command::deserialize(stream);
|
||||
}
|
||||
|
||||
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::CLIENT_HELLO)
|
||||
msg = std::make_shared<client_hello>();
|
||||
else if (type == message::SERVER_HELLO)
|
||||
msg = std::make_shared<server_hello>();
|
||||
else if (type == message::FRAME_INFO)
|
||||
msg = std::make_shared<frame_info>();
|
||||
else if (type == message::REQUEST_COMMAND)
|
||||
msg = std::make_shared<request_command>();
|
||||
|
||||
msg->deserialize(stream);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void network::serialize_message(const message &msg, std::ostream &stream)
|
||||
{
|
||||
sn_utils::ls_uint16(stream, (uint16_t)msg.type);
|
||||
msg.serialize(stream);
|
||||
}
|
||||
73
network/message.h
Normal file
73
network/message.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include "network/message.h"
|
||||
#include "command.h"
|
||||
#include <queue>
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct message
|
||||
{
|
||||
enum type_e
|
||||
{
|
||||
CLIENT_HELLO = 0,
|
||||
SERVER_HELLO,
|
||||
FRAME_INFO,
|
||||
REQUEST_COMMAND,
|
||||
TYPE_MAX
|
||||
};
|
||||
|
||||
type_e type;
|
||||
|
||||
message(type_e t) : type(t) {}
|
||||
virtual void serialize(std::ostream &stream) const {}
|
||||
virtual void deserialize(std::istream &stream) {}
|
||||
};
|
||||
|
||||
struct client_hello : public message
|
||||
{
|
||||
client_hello() : message(CLIENT_HELLO) {}
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
|
||||
int32_t version;
|
||||
uint32_t start_packet;
|
||||
};
|
||||
|
||||
struct server_hello : public message
|
||||
{
|
||||
server_hello() : message(SERVER_HELLO) {}
|
||||
|
||||
uint32_t seed;
|
||||
int64_t timestamp;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
};
|
||||
|
||||
struct request_command : public message
|
||||
{
|
||||
request_command(type_e type) : message(type) {}
|
||||
request_command() : message(REQUEST_COMMAND) {}
|
||||
|
||||
command_queue::commands_map commands;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
};
|
||||
|
||||
struct frame_info : public request_command
|
||||
{
|
||||
frame_info() : request_command(FRAME_INFO) {}
|
||||
|
||||
double render_dt;
|
||||
double dt;
|
||||
double sync;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
};
|
||||
|
||||
std::shared_ptr<message> deserialize_message(std::istream &stream);
|
||||
void serialize_message(const message &msg, std::ostream &stream);
|
||||
} // namespace network
|
||||
269
network/network.cpp
Normal file
269
network/network.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "stdafx.h"
|
||||
#include "network/network.h"
|
||||
#include "network/message.h"
|
||||
#include "Logs.h"
|
||||
#include "sn_utils.h"
|
||||
#include "Timer.h"
|
||||
#include "application.h"
|
||||
#include "Globals.h"
|
||||
|
||||
namespace network {
|
||||
|
||||
backend_list_t& backend_list() {
|
||||
// HACK: static initialization order fiasco fix
|
||||
// NOTE: potential static deinitialization order fiasco
|
||||
static backend_list_t backend_list;
|
||||
return backend_list;
|
||||
}
|
||||
|
||||
}
|
||||
// connection
|
||||
|
||||
void network::connection::disconnect() {
|
||||
WriteLog("net: peer dropped", logtype::net);
|
||||
state = DEAD;
|
||||
}
|
||||
|
||||
void network::connection::set_handler(std::function<void (const message &)> handler) {
|
||||
message_handler = handler;
|
||||
}
|
||||
|
||||
network::connection::connection(bool client, size_t counter) {
|
||||
packet_counter = counter;
|
||||
is_client = client;
|
||||
state = AWAITING_HELLO;
|
||||
}
|
||||
|
||||
void network::connection::connected()
|
||||
{
|
||||
WriteLog("net: socket connected", logtype::net);
|
||||
|
||||
if (is_client) {
|
||||
client_hello msg;
|
||||
msg.version = 1;
|
||||
msg.start_packet = packet_counter;
|
||||
send_message(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void network::connection::catch_up()
|
||||
{
|
||||
backbuffer->seekg(backbuffer_pos);
|
||||
|
||||
std::vector<std::shared_ptr<message>> messages;
|
||||
|
||||
for (int i = 0; i < CATCHUP_PACKETS; i++) {
|
||||
if (backbuffer->peek() == EOF) {
|
||||
send_messages(messages);
|
||||
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(msg);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!is_client && state == CATCHING_UP) {
|
||||
catch_up();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------
|
||||
|
||||
// server
|
||||
network::server::server(std::shared_ptr<std::istream> buf) : backbuffer(buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void network::server::push_delta(const frame_info &msg)
|
||||
{
|
||||
for (auto it = clients.begin(); it != clients.end(); ) {
|
||||
if ((*it)->state == connection::DEAD) {
|
||||
it = clients.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((*it)->state == connection::ACTIVE)
|
||||
(*it)->send_message(msg);
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
command_queue::commands_map network::server::pop_commands()
|
||||
{
|
||||
command_queue::commands_map map(client_commands_queue);
|
||||
client_commands_queue.clear();
|
||||
return map;
|
||||
}
|
||||
|
||||
void network::server::handle_message(std::shared_ptr<connection> conn, const message &msg)
|
||||
{
|
||||
if (msg.type == message::TYPE_MAX)
|
||||
{
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type == message::CLIENT_HELLO) {
|
||||
auto cmd = dynamic_cast<const client_hello&>(msg);
|
||||
|
||||
if (cmd.version != 1 // wrong version
|
||||
|| !Global.ready_to_load) { // not ready yet
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
server_hello reply;
|
||||
reply.seed = Global.random_seed;
|
||||
reply.timestamp = Global.starting_timestamp;
|
||||
conn->state = connection::CATCHING_UP;
|
||||
conn->backbuffer = backbuffer;
|
||||
conn->backbuffer_pos = 0;
|
||||
conn->packet_counter = cmd.start_packet;
|
||||
|
||||
conn->send_message(reply);
|
||||
|
||||
WriteLog("net: client accepted", logtype::net);
|
||||
}
|
||||
else if (msg.type == message::REQUEST_COMMAND) {
|
||||
auto cmd = dynamic_cast<const request_command&>(msg);
|
||||
|
||||
for (auto const &kv : cmd.commands)
|
||||
client_commands_queue.emplace(kv);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
void network::client::update()
|
||||
{
|
||||
if (conn && conn->state == connection::DEAD) {
|
||||
conn.reset();
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
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(int counter)
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (counter == 1) {
|
||||
frame_time = now - last_frame;
|
||||
last_frame = now;
|
||||
}
|
||||
|
||||
if (delta_queue.empty()) {
|
||||
// buffer underflow
|
||||
return std::tuple<double, double,
|
||||
command_queue::commands_map>(0.0, 0.0, command_queue::commands_map());
|
||||
}
|
||||
|
||||
|
||||
float size = delta_queue.size() - consume_counter;
|
||||
auto entry = delta_queue.front();
|
||||
float mult = entry.render_dt / std::chrono::duration_cast<std::chrono::duration<float>>(frame_time).count();
|
||||
|
||||
if (counter == 1 && size < MAX_BUFFER_SIZE * 2.0f) {
|
||||
last_target = last_target * TARGET_MIX +
|
||||
(std::min(TARGET_MIN + jitteriness * JITTERINESS_MULTIPIER, MAX_BUFFER_SIZE)) * (1.0f - TARGET_MIX);
|
||||
float diff = size - last_target;
|
||||
jitteriness = std::max(jitteriness * JITTERINESS_MIX, std::abs(diff));
|
||||
|
||||
float speed = 1.0f + diff * CONSUME_MULTIPIER;
|
||||
|
||||
consume_counter += speed;
|
||||
}
|
||||
|
||||
float last_rcv_diff = std::chrono::duration_cast<std::chrono::duration<float>>(now - last_rcv).count();
|
||||
|
||||
if (size > MAX_BUFFER_SIZE || consume_counter > mult || last_rcv_diff > 1.0f) {
|
||||
if (consume_counter > mult) {
|
||||
consume_counter = std::clamp(consume_counter - mult, -MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
delta_queue.pop();
|
||||
|
||||
return std::make_tuple(entry.dt, entry.sync, entry.commands);
|
||||
} else {
|
||||
// nothing to push
|
||||
return std::tuple<double, double,
|
||||
command_queue::commands_map>(0.0, 0.0, command_queue::commands_map());
|
||||
}
|
||||
}
|
||||
|
||||
void network::client::send_commands(command_queue::commands_map commands)
|
||||
{
|
||||
if (!conn || conn->state == connection::DEAD || commands.empty())
|
||||
return;
|
||||
// eh, maybe queue lost messages
|
||||
|
||||
request_command msg;
|
||||
msg.commands = commands;
|
||||
|
||||
conn->send_message(msg);
|
||||
}
|
||||
|
||||
void network::client::handle_message(std::shared_ptr<connection> conn, const message &msg)
|
||||
{
|
||||
if (msg.type >= message::TYPE_MAX)
|
||||
{
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type == message::SERVER_HELLO) {
|
||||
auto cmd = dynamic_cast<const server_hello&>(msg);
|
||||
conn->state = connection::ACTIVE;
|
||||
|
||||
if (!Global.ready_to_load) {
|
||||
Global.random_seed = cmd.seed;
|
||||
Global.random_engine.seed(Global.random_seed);
|
||||
Global.starting_timestamp = cmd.timestamp;
|
||||
Global.ready_to_load = true;
|
||||
} else if (Global.random_seed != cmd.seed) {
|
||||
ErrorLog("net: seed mismatch", logtype::net);
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLog("net: accept received", logtype::net);
|
||||
}
|
||||
|
||||
if (conn->state != connection::ACTIVE)
|
||||
return;
|
||||
|
||||
if (msg.type == message::FRAME_INFO) {
|
||||
resume_frame_counter++;
|
||||
|
||||
auto delta = dynamic_cast<const frame_info&>(msg);
|
||||
delta_queue.push(delta);
|
||||
last_rcv = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------
|
||||
122
network/network.h
Normal file
122
network/network.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
#include "network/message.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace network
|
||||
{
|
||||
//m7todo: separate client/server connection class?
|
||||
class connection
|
||||
{
|
||||
friend class server;
|
||||
friend class client;
|
||||
|
||||
private:
|
||||
const int CATCHUP_PACKETS = 300;
|
||||
|
||||
bool is_client;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<std::istream> backbuffer;
|
||||
size_t backbuffer_pos;
|
||||
size_t packet_counter;
|
||||
|
||||
void send_complete(std::shared_ptr<std::string> buf);
|
||||
void catch_up();
|
||||
|
||||
public:
|
||||
std::function<void(const message &msg)> message_handler;
|
||||
|
||||
virtual void connected() = 0;
|
||||
virtual void send_message(const message &msg) = 0;
|
||||
virtual void send_messages(const std::vector<std::shared_ptr<message>> &messages) = 0;
|
||||
|
||||
connection(bool client = false, size_t counter = 0);
|
||||
void set_handler(std::function<void(const message &msg)> handler);
|
||||
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
enum peer_state {
|
||||
AWAITING_HELLO,
|
||||
CATCHING_UP,
|
||||
ACTIVE,
|
||||
DEAD
|
||||
};
|
||||
peer_state state;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<std::istream> backbuffer;
|
||||
|
||||
protected:
|
||||
void handle_message(std::shared_ptr<connection> conn, const message &msg);
|
||||
|
||||
std::vector<std::shared_ptr<connection>> clients;
|
||||
|
||||
command_queue::commands_map client_commands_queue;
|
||||
|
||||
public:
|
||||
server(std::shared_ptr<std::istream> buf);
|
||||
void push_delta(const frame_info &msg);
|
||||
command_queue::commands_map pop_commands();
|
||||
};
|
||||
|
||||
class client
|
||||
{
|
||||
protected:
|
||||
virtual void connect() = 0;
|
||||
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;
|
||||
const float MAX_BUFFER_SIZE = 60.0f;
|
||||
const float JITTERINESS_MIX = 0.998f;
|
||||
const float TARGET_MIN = 2.0f;
|
||||
const float TARGET_MIX = 0.98f;
|
||||
const float JITTERINESS_MULTIPIER = 2.0f;
|
||||
const float CONSUME_MULTIPIER = 0.05f;
|
||||
|
||||
std::queue<frame_info> delta_queue;
|
||||
|
||||
float last_target = 20.0f;
|
||||
float jitteriness = 1.0f;
|
||||
float consume_counter = 0.0f;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point last_rcv;
|
||||
std::chrono::high_resolution_clock::time_point last_frame;
|
||||
std::chrono::high_resolution_clock::duration frame_time;
|
||||
|
||||
public:
|
||||
void update();
|
||||
std::tuple<double, double, command_queue::commands_map> get_next_delta(int counter);
|
||||
void send_commands(command_queue::commands_map commands);
|
||||
int get_frame_counter() {
|
||||
return resume_frame_counter;
|
||||
}
|
||||
int get_awaiting_frames() {
|
||||
return delta_queue.size();
|
||||
}
|
||||
};
|
||||
|
||||
class backend_manager
|
||||
{
|
||||
public:
|
||||
virtual std::shared_ptr<server> create_server(std::shared_ptr<std::fstream>, const std::string &conf) = 0;
|
||||
virtual std::shared_ptr<client> create_client(const std::string &conf) = 0;
|
||||
virtual void update() = 0;
|
||||
};
|
||||
|
||||
// HACK: static initialization order fiasco fix
|
||||
// extern std::unordered_map<std::string, backend_manager*> backend_list;
|
||||
using backend_list_t = std::unordered_map<std::string, backend_manager*>;
|
||||
backend_list_t& backend_list();
|
||||
}
|
||||
@@ -37,20 +37,33 @@ scenarioloader_mode::init() {
|
||||
bool
|
||||
scenarioloader_mode::update() {
|
||||
|
||||
WriteLog( "\nLoading scenario \"" + Global.SceneryFile + "\"..." );
|
||||
if (!Global.ready_to_load)
|
||||
// waiting for network connection
|
||||
return true;
|
||||
|
||||
auto timestart = std::chrono::system_clock::now();
|
||||
if (!state) {
|
||||
WriteLog("using simulation seed: " + std::to_string(Global.random_seed), logtype::generic);
|
||||
|
||||
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
|
||||
Application.pop_mode();
|
||||
Application.push_mode( eu07_application::mode::driver );
|
||||
}
|
||||
else {
|
||||
ErrorLog( "Bad init: scenario loading failed" );
|
||||
Application.pop_mode();
|
||||
}
|
||||
WriteLog( "\nLoading scenario \"" + Global.SceneryFile + "\"..." );
|
||||
|
||||
timestart = std::chrono::system_clock::now();
|
||||
state = simulation::State.deserialize_begin(Global.SceneryFile);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -75,6 +88,6 @@ scenarioloader_mode::enter() {
|
||||
void
|
||||
scenarioloader_mode::exit() {
|
||||
|
||||
simulation::Time.init();
|
||||
simulation::Time.init( Global.starting_timestamp );
|
||||
simulation::Environment.init();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -32,6 +35,8 @@ public:
|
||||
// input handlers
|
||||
void
|
||||
on_key( int const Key, int const Scancode, int const Action, int const Mods ) override { ; }
|
||||
void
|
||||
on_char( unsigned int const Char ) override { ; }
|
||||
void
|
||||
on_cursor_pos( double const Horizontal, double const Vertical ) override { ; }
|
||||
void
|
||||
@@ -40,4 +45,6 @@ public:
|
||||
on_scroll( double const Xoffset, double const Yoffset ) override { ; }
|
||||
void
|
||||
on_event_poll() override { ; }
|
||||
bool
|
||||
is_command_processor() override { return false; }
|
||||
};
|
||||
|
||||
37
scene.cpp
37
scene.cpp
@@ -35,7 +35,7 @@ basic_cell::on_click( TAnimModel const *Instance ) {
|
||||
if( ( launcher->name() == Instance->name() )
|
||||
&& ( glm::length2( launcher->location() - Instance->location() ) < launcher->dRadius )
|
||||
&& ( true == launcher->check_conditions() ) ) {
|
||||
launch_event( launcher );
|
||||
launch_event( launcher, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,11 +123,13 @@ basic_cell::update_events() {
|
||||
|
||||
// event launchers
|
||||
for( auto *launcher : m_eventlaunchers ) {
|
||||
if( ( true == ( launcher->check_activation() && launcher->check_conditions() ) )
|
||||
&& ( ( launcher->dRadius < 0.0 )
|
||||
|| ( SquareMagnitude( launcher->location() - Global.pCamera.Pos ) < launcher->dRadius ) ) ) {
|
||||
|
||||
launch_event( launcher );
|
||||
if( launcher->check_conditions()
|
||||
&& ( launcher->dRadius < 0.0
|
||||
|| SquareMagnitude( launcher->location() - Global.pCamera.Pos ) < launcher->dRadius ) ) {
|
||||
if( launcher->check_activation() )
|
||||
launch_event( launcher, true );
|
||||
if( launcher->check_activation_key() )
|
||||
launch_event( launcher, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,16 +610,19 @@ basic_cell::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
|
||||
// executes event assigned to specified launcher
|
||||
void
|
||||
basic_cell::launch_event( TEventLauncher *Launcher ) {
|
||||
|
||||
WriteLog( "Eventlauncher " + Launcher->name() );
|
||||
if( ( true == Global.shiftState )
|
||||
&& ( Launcher->Event2 != nullptr ) ) {
|
||||
simulation::Events.AddToQuery( Launcher->Event2, nullptr );
|
||||
}
|
||||
else if( Launcher->Event1 ) {
|
||||
simulation::Events.AddToQuery( Launcher->Event1, nullptr );
|
||||
}
|
||||
basic_cell::launch_event( TEventLauncher *Launcher, bool local_only ) {
|
||||
WriteLog( "Eventlauncher: " + Launcher->name() );
|
||||
if (!local_only) {
|
||||
if( Launcher->Event1 ) {
|
||||
simulation::Events.AddToQuery( Launcher->Event1, nullptr );
|
||||
}
|
||||
} else {
|
||||
command_relay commandrelay;
|
||||
if (Global.shiftState && Launcher->Event2 != nullptr)
|
||||
commandrelay.post(user_command::queueevent, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &Launcher->Event2->name());
|
||||
else if (Launcher->Event1)
|
||||
commandrelay.post(user_command::queueevent, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &Launcher->Event1->name());
|
||||
}
|
||||
}
|
||||
|
||||
// adjusts cell bounding area to enclose specified node
|
||||
|
||||
2
scene.h
2
scene.h
@@ -173,7 +173,7 @@ private:
|
||||
using memorycell_sequence = std::vector<TMemCell *>;
|
||||
// methods
|
||||
void
|
||||
launch_event( TEventLauncher *Launcher );
|
||||
launch_event(TEventLauncher *Launcher, bool local_only);
|
||||
void
|
||||
enclose_area( scene::basic_node *Node );
|
||||
// members
|
||||
|
||||
@@ -342,6 +342,10 @@ public:
|
||||
group( scene::group_handle Group );
|
||||
scene::group_handle
|
||||
group() const;
|
||||
void
|
||||
mark_dirty() { m_dirty = true; }
|
||||
bool
|
||||
dirty() const { return m_dirty; }
|
||||
|
||||
protected:
|
||||
// members
|
||||
@@ -351,6 +355,7 @@ protected:
|
||||
double m_rangesquaredmax { 0.0 }; // visibility range, max
|
||||
bool m_visible { true }; // visibility flag
|
||||
std::string m_name;
|
||||
bool m_dirty { false };
|
||||
|
||||
private:
|
||||
// methods
|
||||
|
||||
274
simulation.cpp
274
simulation.cpp
@@ -25,6 +25,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "particles.h"
|
||||
#include "scene.h"
|
||||
#include "Train.h"
|
||||
#include "application.h"
|
||||
|
||||
namespace simulation {
|
||||
|
||||
@@ -37,18 +38,26 @@ powergridsource_table Powergrid;
|
||||
sound_table Sounds;
|
||||
instance_table Instances;
|
||||
vehicle_table Vehicles;
|
||||
train_table Trains;
|
||||
light_array Lights;
|
||||
particle_manager Particles;
|
||||
|
||||
scene::basic_region *Region { nullptr };
|
||||
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
|
||||
@@ -92,9 +101,7 @@ state_manager::init_scripting_interface() {
|
||||
void
|
||||
state_manager::update( double const Deltatime, int Iterationcount ) {
|
||||
// aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeń
|
||||
if (Deltatime == 0.0) {
|
||||
return;
|
||||
}
|
||||
if (Deltatime == 0.0) { return; }
|
||||
|
||||
auto const totaltime { Deltatime * Iterationcount };
|
||||
// NOTE: we perform animations first, as they can determine factors like contact with powergrid
|
||||
@@ -166,6 +173,261 @@ state_manager::update_scripting_interface() {
|
||||
*m_scriptinginterface.date = *date;
|
||||
}
|
||||
|
||||
void state_manager::process_commands() {
|
||||
command_data commanddata;
|
||||
while( Commands.pop( commanddata, (uint32_t)command_target::simulation )) {
|
||||
if (commanddata.command == user_command::consistreleaser) {
|
||||
TDynamicObject *found_vehicle = simulation::Vehicles.find(commanddata.payload);
|
||||
TDynamicObject *vehicle = found_vehicle;
|
||||
|
||||
while (vehicle) {
|
||||
vehicle->MoverParameters->Hamulec->Releaser(commanddata.action != GLFW_RELEASE ? 1 : 0);
|
||||
vehicle = vehicle->Next();
|
||||
}
|
||||
|
||||
vehicle = found_vehicle;
|
||||
while (vehicle) {
|
||||
vehicle->MoverParameters->Hamulec->Releaser(commanddata.action != GLFW_RELEASE ? 1 : 0);
|
||||
vehicle = vehicle->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
if (commanddata.action == GLFW_RELEASE)
|
||||
continue;
|
||||
|
||||
if (commanddata.command == user_command::debugtoggle)
|
||||
DebugModeFlag = !DebugModeFlag;
|
||||
|
||||
if (commanddata.command == user_command::pausetoggle) {
|
||||
if( Global.iPause & 1 ) {
|
||||
// jeśli pauza startowa
|
||||
// odpauzowanie, gdy po wczytaniu miało nie startować
|
||||
Global.iPause ^= 1;
|
||||
}
|
||||
else {
|
||||
Global.iPause ^= 2; // zmiana stanu zapauzowania
|
||||
}
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::focuspauseset) {
|
||||
if( commanddata.param1 == 1.0 )
|
||||
Global.iPause &= ~4; // odpauzowanie, gdy jest na pierwszym planie
|
||||
else
|
||||
Global.iPause |= 4; // włączenie pauzy, gdy nieaktywy
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::entervehicle) {
|
||||
// przesiadka do innego pojazdu
|
||||
if (!commanddata.freefly)
|
||||
// only available in free fly mode
|
||||
continue;
|
||||
|
||||
TDynamicObject *dynamic = std::get<TDynamicObject *>( simulation::Region->find_vehicle( commanddata.location, 50, false, false ) );
|
||||
|
||||
if (!dynamic)
|
||||
continue;
|
||||
|
||||
TTrain *train = simulation::Trains.find(dynamic->name());
|
||||
if (train)
|
||||
continue;
|
||||
|
||||
train = new TTrain();
|
||||
if (train->Init(dynamic)) {
|
||||
simulation::Trains.insert(train);
|
||||
}
|
||||
else {
|
||||
delete train;
|
||||
train = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::queueevent) {
|
||||
std::istringstream ss(commanddata.payload);
|
||||
|
||||
std::string event_name;
|
||||
std::string vehicle_name;
|
||||
std::getline(ss, event_name, '%');
|
||||
std::getline(ss, vehicle_name, '%');
|
||||
|
||||
basic_event *ev = Events.FindEvent(event_name);
|
||||
TDynamicObject *vehicle = nullptr;
|
||||
if (!vehicle_name.empty())
|
||||
vehicle = simulation::Vehicles.find(vehicle_name);
|
||||
|
||||
if (ev)
|
||||
Events.AddToQuery(ev, vehicle);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::setlight) {
|
||||
int light = std::round(commanddata.param1);
|
||||
float state = commanddata.param2;
|
||||
TAnimModel *model = simulation::Instances.find(commanddata.payload);
|
||||
if (model)
|
||||
model->LightSet(light, state);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::setdatetime) {
|
||||
int yearday = std::round(commanddata.param1);
|
||||
int minute = std::round(commanddata.param2 * 60.0);
|
||||
simulation::Time.set_time(yearday, minute);
|
||||
|
||||
simulation::Environment.compute_season(yearday);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::setweather) {
|
||||
Global.fFogEnd = commanddata.param1;
|
||||
Global.Overcast = commanddata.param2;
|
||||
|
||||
simulation::Environment.compute_weather();
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::settemperature) {
|
||||
Global.AirTemperature = commanddata.param1;
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::insertmodel) {
|
||||
std::istringstream ss(commanddata.payload);
|
||||
|
||||
std::string name;
|
||||
std::string data;
|
||||
std::getline(ss, name, ':');
|
||||
std::getline(ss, data, ':');
|
||||
|
||||
TAnimModel *model = simulation::State.create_model(data, name, commanddata.location);
|
||||
simulation::State.create_eventlauncher("node -1 0 launcher eventlauncher 0 0 0 " + std::to_string(model->radius())
|
||||
+ " none -10000.0 obstacle_collision end", name + "_snd", commanddata.location);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::deletemodel) {
|
||||
simulation::State.delete_model(simulation::Instances.find(commanddata.payload));
|
||||
simulation::State.delete_eventlauncher(simulation::Events.FindEventlauncher(commanddata.payload + "_snd"));
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::globalradiostop) {
|
||||
simulation::Region->RadioStop( commanddata.location );
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::resetconsist) {
|
||||
TDynamicObject *found_vehicle = simulation::Vehicles.find(commanddata.payload);
|
||||
TDynamicObject *vehicle = found_vehicle;
|
||||
|
||||
while (vehicle) {
|
||||
if (vehicle->Next())
|
||||
vehicle = vehicle->Next();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
while (vehicle) {
|
||||
vehicle->MoverParameters->DamageFlag = 0;
|
||||
vehicle->MoverParameters->EngDmgFlag = 0;
|
||||
vehicle->MoverParameters->V = 0.0;
|
||||
vehicle->MoverParameters->DistCounter = 0.0;
|
||||
vehicle->MoverParameters->WheelFlat = 0.0;
|
||||
vehicle->MoverParameters->AlarmChainFlag = false;
|
||||
vehicle->MoverParameters->OffsetTrackH = 0.0;
|
||||
vehicle->MoverParameters->OffsetTrackV = 0.0;
|
||||
vehicle = vehicle->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::fillcompressor) {
|
||||
TDynamicObject *vehicle = simulation::Vehicles.find(commanddata.payload);
|
||||
vehicle->MoverParameters->CompressedVolume = 8.0f * vehicle->MoverParameters->VeselVolume;
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::dynamicmove) {
|
||||
TDynamicObject *vehicle = simulation::Vehicles.find(commanddata.payload);
|
||||
if (vehicle)
|
||||
vehicle->move_set(commanddata.param1);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::consistteleport) {
|
||||
std::istringstream ss(commanddata.payload);
|
||||
|
||||
std::string track_name;
|
||||
std::string vehicle_name;
|
||||
std::getline(ss, vehicle_name, '%');
|
||||
std::getline(ss, track_name, '%');
|
||||
|
||||
TTrack *track = simulation::Paths.find(track_name);
|
||||
TDynamicObject *vehicle = simulation::Vehicles.find(vehicle_name);
|
||||
|
||||
while (vehicle) {
|
||||
if (vehicle->Next())
|
||||
vehicle = vehicle->Next();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
double offset = 0.0;
|
||||
|
||||
while (vehicle) {
|
||||
offset += vehicle->MoverParameters->Dim.L;
|
||||
vehicle->place_on_track(track, offset, false);
|
||||
vehicle = vehicle->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::spawntrainset) {
|
||||
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::pullalarmchain) {
|
||||
TDynamicObject *vehicle = simulation::Vehicles.find(commanddata.payload);
|
||||
if (vehicle)
|
||||
vehicle->MoverParameters->AlarmChainSwitch(true);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::sendaicommand) {
|
||||
std::istringstream ss(commanddata.payload);
|
||||
|
||||
std::string vehicle_name;
|
||||
std::string command;
|
||||
std::getline(ss, vehicle_name, '%');
|
||||
std::getline(ss, command, '%');
|
||||
|
||||
TDynamicObject *vehicle = simulation::Vehicles.find(vehicle_name);
|
||||
glm::dvec3 location = commanddata.location;
|
||||
if (vehicle && vehicle->Mechanik)
|
||||
vehicle->Mechanik->PutCommand(command, commanddata.param1, commanddata.param2, &location);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::quitsimulation) {
|
||||
Application.queue_quit();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TAnimModel * state_manager::create_model(const std::string &src, const std::string &name, const glm::dvec3 &position) {
|
||||
return m_serializer.create_model(src, name, position);
|
||||
}
|
||||
|
||||
TEventLauncher * state_manager::create_eventlauncher(const std::string &src, const std::string &name, const glm::dvec3 &position) {
|
||||
return m_serializer.create_eventlauncher(src, name, position);
|
||||
}
|
||||
|
||||
void state_manager::delete_model(TAnimModel *model) {
|
||||
Region->erase(model);
|
||||
Instances.purge(model);
|
||||
}
|
||||
|
||||
void state_manager::delete_eventlauncher(TEventLauncher *launcher) {
|
||||
launcher->dRadius = 0.0f; // disable it
|
||||
}
|
||||
|
||||
// passes specified sound to all vehicles within range as a radio message broadcasted on specified channel
|
||||
void
|
||||
radio_message( sound_source *Message, int const Channel ) {
|
||||
|
||||
28
simulation.h
28
simulation.h
@@ -27,9 +27,27 @@ public:
|
||||
update_clocks();
|
||||
void
|
||||
update_scripting_interface();
|
||||
// restores simulation data from specified file. returns: true on success, false otherwise
|
||||
bool
|
||||
deserialize( std::string const &Scenariofile );
|
||||
// process input commands
|
||||
void
|
||||
process_commands();
|
||||
// create model from node string
|
||||
TAnimModel *
|
||||
create_model(const std::string &src, const std::string &name, const glm::dvec3 &position);
|
||||
// create eventlauncher from node string
|
||||
TEventLauncher *
|
||||
create_eventlauncher(const std::string &src, const std::string &name, const glm::dvec3 &position);
|
||||
// delete TAnimModel instance
|
||||
void
|
||||
delete_model(TAnimModel *model);
|
||||
// delete TEventLauncher instance
|
||||
void
|
||||
delete_eventlauncher(TEventLauncher *launcher);
|
||||
// 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;
|
||||
@@ -54,14 +72,18 @@ extern powergridsource_table Powergrid;
|
||||
extern sound_table Sounds;
|
||||
extern instance_table Instances;
|
||||
extern vehicle_table Vehicles;
|
||||
extern train_table Trains;
|
||||
extern light_array Lights;
|
||||
extern particle_manager Particles;
|
||||
|
||||
extern scene::basic_region *Region;
|
||||
extern TTrain *Train;
|
||||
|
||||
extern uint16_t prev_train_id;
|
||||
extern bool is_ready;
|
||||
|
||||
struct deserializer_state;
|
||||
|
||||
} // simulation
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -31,8 +31,8 @@ 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 );
|
||||
@@ -40,93 +40,81 @@ state_serializer::deserialize( std::string const &Scenariofile ) {
|
||||
|
||||
simulation::State.init_scripting_interface();
|
||||
|
||||
// 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;
|
||||
if( ( true == Global.file_binary_terrain )
|
||||
&& ( Scenariofile != "$.scn" ) ) {
|
||||
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 ) ;
|
||||
}
|
||||
// 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; }
|
||||
|
||||
deserialize( scenarioparser, importscratchpad );
|
||||
if( ( true == Global.file_binary_terrain )
|
||||
&& ( 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 );
|
||||
state->scratchpad.binary.terrain = Region->is_scene( Scenariofile ) ;
|
||||
}
|
||||
|
||||
return true;
|
||||
if( false == state->input.ok() )
|
||||
throw invalid_scenery_exception();
|
||||
|
||||
// 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 },
|
||||
{ "assignment", &state_serializer::deserialize_assignment },
|
||||
{ "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 },
|
||||
{ "assignment", &state_serializer::deserialize_assignment },
|
||||
{ "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 },
|
||||
{ "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();
|
||||
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 100 ) {
|
||||
timelast = timenow;
|
||||
glfwPollEvents();
|
||||
auto timenow = std::chrono::steady_clock::now();
|
||||
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 200 ) {
|
||||
Application.set_progress( Input.getProgress(), Input.getFullProgress() );
|
||||
GfxRenderer->Render();
|
||||
return true;
|
||||
}
|
||||
|
||||
token = Input.getToken<std::string>();
|
||||
@@ -136,6 +124,18 @@ state_serializer::deserialize( cParser &Input, scene::scratch_data &Scratchpad )
|
||||
// manually perform scenario initialization
|
||||
deserialize_firstinit( Input, Scratchpad );
|
||||
}
|
||||
/*
|
||||
scene::Groups.update_map();
|
||||
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
|
||||
@@ -1072,6 +1072,60 @@ state_serializer::export_as_text( std::string const &Scenariofile ) const {
|
||||
WriteLog( "Scenery data export done." );
|
||||
}
|
||||
|
||||
TAnimModel *state_serializer::create_model(const std::string &src, const std::string &name, const glm::dvec3 &position) {
|
||||
cParser parser(src);
|
||||
parser.getTokens(); // "node"
|
||||
parser.getTokens(2); // ranges
|
||||
|
||||
scene::node_data nodedata;
|
||||
parser >> nodedata.range_max >> nodedata.range_min;
|
||||
|
||||
parser.getTokens(2); // name, type
|
||||
nodedata.name = name;
|
||||
nodedata.type = "model";
|
||||
|
||||
scene::scratch_data scratch;
|
||||
|
||||
TAnimModel *cloned = deserialize_model(parser, scratch, nodedata);
|
||||
|
||||
if (!cloned)
|
||||
return nullptr;
|
||||
|
||||
cloned->mark_dirty();
|
||||
cloned->location(position);
|
||||
simulation::Instances.insert(cloned);
|
||||
simulation::Region->insert(cloned);
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
TEventLauncher *state_serializer::create_eventlauncher(const std::string &src, const std::string &name, const glm::dvec3 &position) {
|
||||
cParser parser(src);
|
||||
parser.getTokens(); // "node"
|
||||
parser.getTokens(2); // ranges
|
||||
|
||||
scene::node_data nodedata;
|
||||
parser >> nodedata.range_max >> nodedata.range_min;
|
||||
|
||||
parser.getTokens(2); // name, type
|
||||
nodedata.name = name;
|
||||
nodedata.type = "eventlauncher";
|
||||
|
||||
scene::scratch_data scratch;
|
||||
|
||||
TEventLauncher *launcher = deserialize_eventlauncher(parser, scratch, nodedata);
|
||||
|
||||
if (!launcher)
|
||||
return nullptr;
|
||||
|
||||
launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name );
|
||||
launcher->location(position);
|
||||
simulation::Events.insert(launcher);
|
||||
simulation::Region->insert(launcher);
|
||||
|
||||
return launcher;
|
||||
}
|
||||
|
||||
} // simulation
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -14,21 +14,40 @@ 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;
|
||||
// create new model from node stirng
|
||||
TAnimModel * create_model(std::string const &src, std::string const &name, const glm::dvec3 &position);
|
||||
// create new eventlauncher from node stirng
|
||||
TEventLauncher * create_eventlauncher(std::string const &src, std::string const &name, const glm::dvec3 &position);
|
||||
|
||||
private:
|
||||
// methods
|
||||
// restores class data from provided stream
|
||||
void deserialize( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
void deserialize_area( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
void deserialize_assignment( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
|
||||
@@ -20,7 +20,7 @@ scenario_time Time;
|
||||
} // simulation
|
||||
|
||||
void
|
||||
scenario_time::init() {
|
||||
scenario_time::init(std::time_t timestamp) {
|
||||
|
||||
char monthdaycounts[ 2 ][ 13 ] = {
|
||||
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
||||
@@ -32,7 +32,21 @@ scenario_time::init() {
|
||||
auto const requestedhour { ( requestedtime / 60 ) % 24 };
|
||||
auto const requestedminute { requestedtime % 60 };
|
||||
// cache requested elements, if any
|
||||
::GetLocalTime( &m_time );
|
||||
|
||||
if( timestamp != 0 ) {
|
||||
std::tm *tms = std::gmtime( ×tamp );
|
||||
m_time.wYear = tms->tm_year + 1900;
|
||||
m_time.wMonth = tms->tm_mon + 1;
|
||||
m_time.wDayOfWeek = tms->tm_wday;
|
||||
m_time.wDay = tms->tm_mday;
|
||||
m_time.wHour = tms->tm_hour;
|
||||
m_time.wMinute = tms->tm_min;
|
||||
m_time.wSecond = tms->tm_sec;
|
||||
m_time.wMilliseconds = 0;
|
||||
}
|
||||
else {
|
||||
::GetLocalTime( &m_time );
|
||||
}
|
||||
|
||||
if( Global.fMoveLight > 0.f ) {
|
||||
// day and month of the year can be overriden by scenario setup
|
||||
@@ -192,6 +206,13 @@ scenario_time::julian_day() const {
|
||||
return JD;
|
||||
}
|
||||
|
||||
void scenario_time::set_time(int yearday, int minute) {
|
||||
m_yearday = yearday;
|
||||
daymonth(m_time.wDay, m_time.wMonth, m_time.wYear, m_yearday);
|
||||
m_time.wHour = minute / 60;
|
||||
m_time.wMinute = minute % 60;
|
||||
}
|
||||
|
||||
// calculates day of week for provided date
|
||||
int
|
||||
scenario_time::day_of_week( int const Day, int const Month, int const Year ) const {
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
scenario_time() {
|
||||
m_time.wHour = 10; m_time.wMinute = 30; }
|
||||
void
|
||||
init();
|
||||
init(time_t timestamp = 0);
|
||||
void
|
||||
update( double const Deltatime );
|
||||
inline
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
double
|
||||
zone_bias() const {
|
||||
return m_timezonebias; }
|
||||
void
|
||||
set_time(int yearday, int minute);
|
||||
|
||||
private:
|
||||
// converts provided time transition date to regular date
|
||||
|
||||
41
sn_utils.cpp
41
sn_utils.cpp
@@ -38,6 +38,18 @@ int32_t sn_utils::ld_int32(std::istream &s)
|
||||
return reinterpret_cast<int32_t&>(v);
|
||||
}
|
||||
|
||||
// deserialize little endian int64
|
||||
int64_t sn_utils::ld_int64(std::istream &s)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
s.read((char*)buf, 8);
|
||||
uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
|
||||
((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
|
||||
((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
|
||||
((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
|
||||
return reinterpret_cast<int64_t&>(v);
|
||||
}
|
||||
|
||||
// deserialize little endian ieee754 float32
|
||||
float sn_utils::ld_float32(std::istream &s)
|
||||
{
|
||||
@@ -88,6 +100,14 @@ glm::dvec3 sn_utils::d_dvec3(std::istream& s)
|
||||
ld_float64(s) };
|
||||
}
|
||||
|
||||
glm::vec3 sn_utils::d_vec3( std::istream& s)
|
||||
{
|
||||
return {
|
||||
ld_float32(s),
|
||||
ld_float32(s),
|
||||
ld_float32(s) };
|
||||
}
|
||||
|
||||
glm::vec4 sn_utils::d_vec4( std::istream& s)
|
||||
{
|
||||
return {
|
||||
@@ -125,6 +145,20 @@ void sn_utils::ls_int32(std::ostream &s, int32_t v)
|
||||
s.write((char*)buf, 4);
|
||||
}
|
||||
|
||||
void sn_utils::ls_int64(std::ostream &s, int64_t v)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
buf[0] = v;
|
||||
buf[1] = v >> 8;
|
||||
buf[2] = v >> 16;
|
||||
buf[3] = v >> 24;
|
||||
buf[4] = v >> 32;
|
||||
buf[5] = v >> 40;
|
||||
buf[6] = v >> 48;
|
||||
buf[7] = v >> 56;
|
||||
s.write((char*)buf, 8);
|
||||
}
|
||||
|
||||
void sn_utils::ls_float32(std::ostream &s, float t)
|
||||
{
|
||||
uint32_t v = reinterpret_cast<uint32_t&>(t);
|
||||
@@ -173,6 +207,13 @@ void sn_utils::s_dvec3(std::ostream &s, glm::dvec3 const &v)
|
||||
ls_float64(s, v.z);
|
||||
}
|
||||
|
||||
void sn_utils::s_vec3(std::ostream &s, glm::vec3 const &v)
|
||||
{
|
||||
ls_float32(s, v.x);
|
||||
ls_float32(s, v.y);
|
||||
ls_float32(s, v.z);
|
||||
}
|
||||
|
||||
void sn_utils::s_vec4(std::ostream &s, glm::vec4 const &v)
|
||||
{
|
||||
ls_float32(s, v.x);
|
||||
|
||||
@@ -10,20 +10,24 @@ public:
|
||||
static uint16_t ld_uint16(std::istream&);
|
||||
static uint32_t ld_uint32(std::istream&);
|
||||
static int32_t ld_int32(std::istream&);
|
||||
static int64_t ld_int64(std::istream&);
|
||||
static float ld_float32(std::istream&);
|
||||
static double ld_float64(std::istream&);
|
||||
static std::string d_str(std::istream&);
|
||||
static bool d_bool(std::istream&);
|
||||
static glm::dvec3 d_dvec3(std::istream&);
|
||||
static glm::vec3 d_vec3(std::istream&);
|
||||
static glm::vec4 d_vec4(std::istream&);
|
||||
|
||||
static void ls_uint16(std::ostream&, uint16_t);
|
||||
static void ls_uint32(std::ostream&, uint32_t);
|
||||
static void ls_int32(std::ostream&, int32_t);
|
||||
static void ls_int64(std::ostream&, int64_t);
|
||||
static void ls_float32(std::ostream&, float);
|
||||
static void ls_float64(std::ostream&, double);
|
||||
static void s_str(std::ostream&, std::string);
|
||||
static void s_bool(std::ostream&, bool);
|
||||
static void s_dvec3(std::ostream&, glm::dvec3 const &);
|
||||
static void s_vec3(std::ostream&, glm::vec3 const &);
|
||||
static void s_vec4(std::ostream&, glm::vec4 const &);
|
||||
};
|
||||
@@ -194,7 +194,7 @@ sound_source::deserialize_mapping( cParser &Input ) {
|
||||
m_pitchvariation = (
|
||||
variation == 0.0f ?
|
||||
1.0f :
|
||||
0.01f * static_cast<float>( Random( 100.0 - variation, 100.0 + variation ) ) );
|
||||
0.01f * static_cast<float>( LocalRandom( 100.0 - variation, 100.0 + variation ) ) );
|
||||
}
|
||||
else if( key == "startoffset:" ) {
|
||||
m_startoffset =
|
||||
@@ -353,7 +353,7 @@ sound_source::play( int const Flags ) {
|
||||
|
||||
// initialize emitter-specific pitch variation if it wasn't yet set
|
||||
if( m_pitchvariation == 0.f ) {
|
||||
m_pitchvariation = 0.01f * static_cast<float>( Random( 97.5, 102.5 ) );
|
||||
m_pitchvariation = 0.01f * static_cast<float>( LocalRandom( 97.5, 102.5 ) );
|
||||
}
|
||||
/*
|
||||
if( ( ( m_flags & sound_flags::exclusive ) != 0 )
|
||||
|
||||
@@ -28,7 +28,7 @@ Copyright (C) 2007-2014 Maciej Cierniak
|
||||
#include "parser.h"
|
||||
|
||||
bool DebugModeFlag = false;
|
||||
bool FreeFlyModeFlag = false;
|
||||
bool FreeFlyModeFlag = true;
|
||||
bool EditorModeFlag = false;
|
||||
bool DebugCameraFlag = false;
|
||||
bool DebugTractionFlag = false;
|
||||
@@ -106,8 +106,8 @@ bool ClearFlag( int &Flag, int const Value ) {
|
||||
|
||||
double Random(double a, double b)
|
||||
{
|
||||
std::uniform_real_distribution<> dis(a, b);
|
||||
return dis(Global.random_engine);
|
||||
uint32_t val = Global.random_engine();
|
||||
return interpolate(a, b, (double)val / Global.random_engine.max());
|
||||
}
|
||||
|
||||
double LocalRandom(double a, double b)
|
||||
|
||||
@@ -313,8 +313,8 @@ template <class Iterator_, class VecType_>
|
||||
void
|
||||
bounding_box( VecType_ &Mincorner, VecType_ &Maxcorner, Iterator_ First, Iterator_ Last ) {
|
||||
|
||||
Mincorner = VecType_( typename std::numeric_limits<VecType_::value_type>::max() );
|
||||
Maxcorner = VecType_( typename std::numeric_limits<VecType_::value_type>::lowest() );
|
||||
Mincorner = VecType_( std::numeric_limits<typename VecType_::value_type>::max() );
|
||||
Maxcorner = VecType_( std::numeric_limits<typename VecType_::value_type>::lowest() );
|
||||
|
||||
std::for_each(
|
||||
First, Last,
|
||||
|
||||
Reference in New Issue
Block a user