mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 22:39:19 +02:00
TTrain::id() numbers cabs with a local counter, in the order each peer happens to create them, so it cannot address a vehicle across the session. Neither was there any way for a player to see what is available to drive. - network/entities: PeerId (the host is peer 1, remote peers are numbered from 2, so it is a participant rather than a special case) and NetworkEntityId, plus a registry that numbers the drivable vehicles of the loaded scenario in name order and resolves ids back to TDynamicObject - the server owns the numbering and publishes it as VEHICLE_LIST - name, crew occupancy, crew capacity, AI state, availability - on every change and at least every 120 frames, out of band so it stays out of the replay backbuffer; a client binds the received ids to its own copy of the scenario by name - SERVER_HELLO hands the joining peer its PeerId - a multiplayer lobby listing that roster, with vehicles taken by other players shown as such rather than hidden; entering one goes through the existing entervehicle command, the vehicle is never moved The lobby lives inside the driver mode instead of a mode of its own: the world has to keep running while somebody is choosing (a client stops consuming the server's frames the moment its mode is not a command processor), and the same screen is what a player needs to change vehicles later on. Crew occupancy is still derived from the presence of a cab instance; the real crew model, claim handshake and AI takeover follow in stage 3.
149 lines
3.8 KiB
C++
149 lines
3.8 KiB
C++
/*
|
|
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/entities.h"
|
|
|
|
#include "simulation/simulation.h"
|
|
#include "vehicle/DynObj.h"
|
|
#include "vehicle/Driver.h"
|
|
#include "vehicle/Train.h"
|
|
#include "utilities/Logs.h"
|
|
|
|
namespace network
|
|
{
|
|
|
|
entity_registry Entities;
|
|
|
|
PeerId allocate_peer_id()
|
|
{
|
|
static PeerId next = PEER_FIRST_REMOTE;
|
|
return next++;
|
|
}
|
|
|
|
bool is_drivable(TDynamicObject const *Vehicle)
|
|
{
|
|
if (Vehicle == nullptr || Vehicle->MoverParameters == nullptr)
|
|
return false;
|
|
|
|
// anything with its own drive, plus anything the scenario already put a driver in
|
|
// (this covers driving trailers of multiple units, which carry no engine of their own)
|
|
// TODO: a cab count exposed by TMoverParameters would be a more precise test
|
|
return Vehicle->MoverParameters->EngineType != TEngineType::None || Vehicle->Mechanik != nullptr;
|
|
}
|
|
|
|
void entity_registry::clear()
|
|
{
|
|
m_entries.clear();
|
|
m_byname.clear();
|
|
m_byid.clear();
|
|
}
|
|
|
|
void entity_registry::build()
|
|
{
|
|
clear();
|
|
|
|
std::vector<std::string> names;
|
|
for (TDynamicObject *vehicle : simulation::Vehicles.sequence())
|
|
{
|
|
if (!is_drivable(vehicle))
|
|
continue;
|
|
names.emplace_back(vehicle->name());
|
|
}
|
|
|
|
// name order keeps the numbering reproducible regardless of scenario spawn order
|
|
std::sort(names.begin(), names.end());
|
|
|
|
for (auto const &name : names)
|
|
{
|
|
if (m_byname.find(name) != m_byname.end())
|
|
{
|
|
// a malformed scenario can hold duplicate names; only the first one is addressable
|
|
ErrorLog("net: duplicate vehicle name \"" + name + "\", not assigning a second network id", logtype::net);
|
|
continue;
|
|
}
|
|
|
|
vehicle_entry entry;
|
|
entry.id = static_cast<NetworkEntityId>(m_entries.size() + 1);
|
|
entry.name = name;
|
|
m_entries.emplace_back(entry);
|
|
m_byname.emplace(name, entry.id);
|
|
}
|
|
|
|
reindex();
|
|
refresh();
|
|
|
|
WriteLog("net: assigned network ids to " + std::to_string(m_entries.size()) + " drivable vehicles", logtype::net);
|
|
}
|
|
|
|
void entity_registry::adopt(std::vector<vehicle_entry> const &Entries)
|
|
{
|
|
m_entries = Entries;
|
|
m_byname.clear();
|
|
for (auto const &entry : m_entries)
|
|
m_byname.emplace(entry.name, entry.id);
|
|
reindex();
|
|
}
|
|
|
|
void entity_registry::reindex()
|
|
{
|
|
m_byid.clear();
|
|
for (size_t i = 0; i < m_entries.size(); ++i)
|
|
m_byid.emplace(m_entries[i].id, i);
|
|
}
|
|
|
|
void entity_registry::refresh()
|
|
{
|
|
for (auto &entry : m_entries)
|
|
{
|
|
TDynamicObject const *vehicle = resolve(entry.id);
|
|
|
|
entry.ai_active = (vehicle != nullptr && vehicle->Mechanik != nullptr && vehicle->Mechanik->AIControllFlag);
|
|
// until the crew model of stage 3 is in place, an existing cab instance is the
|
|
// only evidence that somebody took the vehicle over
|
|
entry.crew_count = (simulation::Trains.find(entry.name) != nullptr ? 1 : 0);
|
|
entry.crew_capacity = CREW_CAPACITY;
|
|
entry.claimable = (vehicle != nullptr && entry.crew_count < entry.crew_capacity);
|
|
}
|
|
}
|
|
|
|
NetworkEntityId entity_registry::id_of(std::string const &Name) const
|
|
{
|
|
auto const lookup = m_byname.find(Name);
|
|
return lookup != m_byname.end() ? lookup->second : ENTITY_NONE;
|
|
}
|
|
|
|
NetworkEntityId entity_registry::id_of(TDynamicObject const *Vehicle) const
|
|
{
|
|
return Vehicle != nullptr ? id_of(Vehicle->name()) : ENTITY_NONE;
|
|
}
|
|
|
|
std::string entity_registry::name_of(NetworkEntityId Id) const
|
|
{
|
|
auto const *entry = find(Id);
|
|
return entry != nullptr ? entry->name : std::string();
|
|
}
|
|
|
|
vehicle_entry const *entity_registry::find(NetworkEntityId Id) const
|
|
{
|
|
auto const lookup = m_byid.find(Id);
|
|
return lookup != m_byid.end() ? &m_entries[lookup->second] : nullptr;
|
|
}
|
|
|
|
TDynamicObject *entity_registry::resolve(NetworkEntityId Id) const
|
|
{
|
|
auto const *entry = find(Id);
|
|
if (entry == nullptr)
|
|
return nullptr;
|
|
|
|
return simulation::Vehicles.find(entry->name);
|
|
}
|
|
|
|
} // namespace network
|