17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 23:49:19 +02:00
Commit Graph

3117 Commits

Author SHA1 Message Date
fb94dcfa2b multiplayer: replicate the machinery you hear, not only the machinery you set
A bystander heard nothing of somebody else's converter, compressors, brake air
or relays. Two different reasons for that, and they need two different answers.

The devices whose sound runs for as long as they do - the converter, the main
compressor, the pantograph compressor, the releaser - are outputs of a physics
that a peer only half runs for a vehicle it does not own. Their running flags
travel now, alongside the permits that were already going.

The clicks and hisses are a harder case: MoverParameters::SoundFlag is raised
and wiped inside a single frame, so sampling it when an update happens to be
put together would catch almost none of them. They are collected as the vehicle
finishes with them, just before it clears the field, and the accumulation rides
along with the next update. On arrival they are added to whatever the receiving
peer's own physics produced, and its sound handling plays them once and wipes
the lot, exactly as it does for its own.
2026-09-07 08:48:30 +00:00
d0306f3d4a multiplayer: replicate what a vehicle is heard and seen doing
The horn never reached anybody else. It is not a command a bystander can carry
out: OnCommand_hornlowactivate sets MoverParameters->WarningSignal from inside a
cab, and a peer that is not sitting in that cab has no TTrain for the vehicle,
so the command was dropped on arrival. Only the result of it can travel, and it
was not being sent.

The vehicle state now carries what a vehicle is heard and seen doing rather than
only what it is being told to do: the horn flags, the emergency brake horn and
its alarm chain, the wheel revolutions and the engine revolutions. The last two
are what turn the driven wheels and set the engine note, so somebody else's
locomotive no longer sounds and looks like it is standing still while it rolls
past.

The state also goes out a little sooner, every sixth frame instead of every
tenth: a horn can be pressed and let go inside a couple of frames, and an update
that arrives after it is over is no use.
2026-09-07 08:25:08 +00:00
a0537356bb multiplayer: correct a train by sliding it, not by putting it down again
Reversing on the host turned the whole set around on the client, and the
locomotive ended up looking like something derailed making its way across the
scenery. Both come from the same place: a routine correction was calling
place_on_track(), which sets the axles from scratch and works from the
vehicle's own idea of which way it faces. That is right for a peer joining with
nothing to go on, and wrong for a train that is already running - the placement
reorients it.

A correction now slides the vehicle along the track it is already on, by the
difference between the two bogie offsets, which is the same operation the
physics performs on every step. When the two sides disagree about which piece
of track the vehicle is on, it is left alone until they agree; guessing there
is what put a locomotive beside the rails. place_on_track() is kept for the one
case it suits, a peer joining the session.

Also:

- taking a seat works with the game paused. The whole thing sat inside the
  "has any time passed" branch, so with the world stopped the lobby button
  changed the roster and nothing else happened
- the debug panel gets a Network section, shown only in a session: role, peer,
  tick, bytes and messages each way with a running rate, two charts of kB/s so
  the cost of a session can be read off, how far the worst vehicle is out of
  place, and whether the state digest agrees. The separate "network" overlay
  window is gone - everything it said lives there now, together with the lobby
  and refusal messages that had nowhere to be seen
2026-09-07 07:49:47 +00:00
c0dbf99f1a multiplayer: whoever drives a train runs it, and their ride is the smooth one
Until now the server ran the physics of everything and a client re-ran it from
the server's frame times, corrected a few times a second. That is the worst of
both: the client's own train never moved smoothly, its controls answered a
round trip late, and the two copies still sat metres apart between corrections.

Simulation authority now follows the train:

- the first person aboard a train runs its physics on their own machine, and
  what they produce is what the rest of the session is told about it. A train
  with nobody in it belongs to the host. Every peer works the owner out the
  same way, from the crew of its lowest numbered car, so nobody has to be told
- a peer never applies corrections to a train it runs itself. Its own physics
  is the authority on it, and overwriting that is precisely what made the
  owner's ride stutter
- a client publishes the state of its own trains upstream, ten times a second.
  The server takes it, filtered so a peer can only move the trains it is
  entitled to move, and passes it on to everybody else in its own stream
- so the picture is: your train is smooth for you and may stutter for the
  others; their trains may stutter for you and are smooth for them

Two things follow from it:

- commands for the train you run take effect the moment you press the key
  instead of after a trip to the server and back. They are still sent, so
  everybody else sees them, and the echo is dropped when it returns
- the client no longer replays the server's frame times out of a jitter buffer.
  It runs on its own clock, like the host does, and simply carries out the
  commands as they arrive. Playing back somebody else's frame times meant the
  world ran at the wrong speed whenever the two machines drew at different
  rates, and every buffered frame sat between the player and their controls

Also: the server only sends a frame when it has commands in it, or every tenth
one to carry the tick, instead of one per rendered frame.
2026-09-07 00:19:40 +00:00
0b75e701fc multiplayer: a player works a train, not a car - and put the corrections right
Second live test. Four things, three of them the same mistake seen from
different angles: the session was reasoning about single vehicles where it
should have been reasoning about trains.

- a peer may now operate anything coupled into the set it is sitting in. This
  is what "cabchangebackward rejected: not a member of the crew of that
  vehicle" was: the crew was recorded against one car of a four-car unit, and
  the cab switch addressed another
- every vehicle gets a network id, not only the ones worth driving. Commands
  are addressed by that id, and a player has to be able to walk into any car
  of their train. "peer 3 cannot take en71-017rb_4: no such vehicle in this
  session" was the driving trailer of an EN71 - a full desk, no engine, and
  nobody assigned to it, so the old test missed it. Having a driving position
  is what counts now, engine or not
- the roster groups vehicles into the trains they are coupled into and the
  lobby lists one row per train. Coupling is worked out afresh every time, so
  a train that comes apart becomes two, and because a crew entry sits on the
  car its player occupies, each half keeps the people who were riding in it
- walking to another car of your own train is not a new claim and asks nothing
  of the crew capacity, which counts people per train

Positional correction never actually did anything: it identified a track by
name, and most track in a scenery has none, so the lookup returned nothing and
place_on_track() was never reached. The client sat 178 m out of place for
minutes, reporting four vehicles "put back on the rails" every time while
moving none of them. Tracks now travel as their index in the path table, which
comes from the scenery file and is therefore the same on every peer.

Also from the test:

- the camera jumped in and out of the cab on the client whenever the host
  reversed, because the correction was forcing the server's CabActive and
  CabOccupied onto it. Which cab somebody sits in is theirs alone - two people
  sharing a vehicle each have their own - so it is no longer replicated
- the correction no longer touches the control positions of the train the local
  player is working. Their crew's commands reach every peer on their own, and
  snapping a lever back a fraction of a second after it was moved is what made
  the reverser look dead
- the frame buffer was sized for the old lockstep, up to sixty frames deep.
  Every frame in it is input lag, so it is down to twelve with a target of one
- asking the AI of a vehicle to step aside is given five tries and then said
  once, instead of every thirty frames forever; and a resync request now has a
  real cooldown
2026-09-06 23:39:13 +00:00
a72c3292ad multiplayer: fix the black screen - a cab and is_ready waiting on each other
The renderer draws nothing at all while simulation::is_ready is false, and that
flag only goes up once the local player's cab is built.

The previous commit made walking into a cab a crew request, so the cab of a
claimed vehicle is created by Crews::reconcile() rather than by the command
itself. reconcile() runs from the network housekeeping, which was gated on
simulation::is_ready - so a host started with -v had its entervehicle turned
into a claim, and then the cab waited for is_ready while is_ready waited for
the cab. Black screen on both ends, since a client whose server never gets
going has nothing to show either.

- the housekeeping now waits for Global.simulation_loaded, which says the world
  exists, instead of simulation::is_ready, which says the player has a cab
- in a session, a player with no cab counts as ready: they are an observer in
  the lobby, not a game still loading, and the world has to be drawn for them
- entervehicle is only taken as a crew request when the session actually lists
  that vehicle; anything else stays an ordinary command, so a scenario that
  starts its host in something the roster does not cover still works
2026-09-06 22:54:48 +00:00
9dc78a65dc multiplayer: stream the authoritative state, and let a client walk into a cab
First live test showed three things wrong at once.

A client could not take a vehicle. Walking up to a locomotive and pressing the
enter-vehicle key posts entervehicle, which stage 4 classed as a world-wide
command and refused outright ("reserved for the session host"), so the client
never got a cab and every later control command died locally for want of one.
Walking into a cab is a request for a seat, not a world command: the authority
now turns it into a crew claim, for the host through the same path as for a
remote peer, and answers it the way the lobby button is answered.

Nothing but commands was ever replicated. The server sent dt and the commands
it ran, and the client simulated the rest for itself - so a pantograph the host
raised before the client joined, the contents of the memory cells the signalling
reads, brake pressures and switch positions simply never crossed the wire. The
server now streams the authoritative state a few times a second: whatever has
changed since the last one, and the whole of it every so often so a peer heals
from anything it missed. The snapshot carries the vehicle's inputs - line
breaker, battery, converter and compressor permits, pantograph valves, cab,
lights, doors - and lets the receiving peer derive the rest, plus the memory
cells and the switches.

Desync detection demanded that two state digests be equal, on a design where
the client deliberately runs its own physics. They never can be, so it reported
a desync permanently and asked for a resync every five seconds forever, which
fixed nothing because the next step disagreed again. The digest is a log line
now; what is measured instead is how far out of place the routine correction
found us, and a full resync is only asked for when even that is not catching up.

Also: a consist is put back on the rails as a whole or not at all. Correcting
one vehicle while its neighbour stayed put is what was stretching couplers
until they tore - the last test moved six vehicles out of eight, every time.

And the lobby window collapsed to nothing, because its size_max was left at the
-1 default while size_min was set, so imgui was handed a maximum smaller than
the minimum.
2026-09-06 22:19:28 +00:00
65159b9546 multiplayer: the session imposes the settings that decide the physics
server_hello.config had been a TODO carrying a zero, and every peer ran with
whatever its own eu07.ini happened to say - including a vehicle physics thread
count, which makes the order vehicles are stepped in depend on how threads get
scheduled.

- the handshake now carries a bitfield of the settings that change how the
  physics comes out (full physics, realistic control) and a joining peer takes
  them on, saying in the log what changed
- a multiplayer session runs the vehicle physics on one thread on every peer,
  host included

Settings that only affect what a player sees or how comfortable the controls
feel stay with the player; only what alters the outcome belongs in here.
2026-09-06 19:58:28 +00:00
30fa68cfd8 multiplayer stage 8: state correction and surviving a dropped connection
Two things were still missing once a session ran for a while: a peer that had
drifted apart from the authority had no way back short of restarting, and a
connection that blinked cost the player their seat and handed their vehicle to
the AI while the other driver was still sitting in it.

- a client that disagrees with the authoritative digest for 120 steps in a row
  asks to be put back in line, and the server answers with a fresh snapshot.
  There is a cooldown between requests, so a correction that needs a moment to
  take hold does not turn into a stream of them
- the server hands out a session token with its hello and remembers it. Present
  it again on the next connection and you come back as the same peer, with the
  same id
- a lost connection no longer removes the peer from its crews: the seat is held
  for a minute. Come back in time and nothing changed; stay away and the seat
  is given up properly, handing the vehicle back to the AI only when that
  leaves the crew empty and the AI was driving it in the first place
- after applying a snapshot a peer that finds itself on a crew walks into that
  cab, which is what makes a reconnect land the player back where they were
- the vehicle roster is sent to a joining peer right before its snapshot, so
  the crew section resolves to names instead of waiting for the next broadcast
2026-09-06 19:48:06 +00:00
54d8b112b8 multiplayer stage 7: versioned snapshots instead of replaying the session
Joining meant replaying backbuffer.bin from the very first frame of the
session, so the longer a session ran, the longer it took to get in - and the
whole history had to be kept for it.

- network/snapshot: a chunked container with a version of its own. Every
  section carries its length, so a peer built before a section existed reads
  a newer snapshot and simply walks past what it does not know. Version 1
  carries the session (tick, clock, weather, the state of the random engine),
  the vehicles (position on the track, speed, direction, controls, brake
  pressures, whether the AI is driving) and the crew roster. Switches,
  memcells, signals and the event queue are the next sections
- joining is now: handshake -> load the scenario -> CLIENT_READY -> the server
  takes a snapshot on the spot and sends it -> the peer goes live. Because the
  snapshot is taken at the moment the peer becomes active there is no backlog
  to replay at all; the frame by frame catch-up is gone
- the server refuses a peer that loaded a different scenario, with a readable
  reason instead of a silent desync
- reconnecting into a session that is already loaded asks for a fresh snapshot
  straight away rather than waiting for a loader that will not run again
- a vehicle is only put back on the rails when it really is somewhere else; the
  arithmetic that turns the recorded bogie offset back into the nose distance
  place_on_track() expects mirrors what that function itself does
- the session log is still written out - it is useful when picking a desync
  apart - but nothing depends on replaying it any more
2026-09-06 19:37:28 +00:00
b255c8120a multiplayer stage 6: a logical simulation tick and a real state digest
The protocol hung off whatever frame the renderer happened to be drawing, and
the only thing standing in for synchronisation was the sum of every vehicle's
coordinates with a random number added on top - a value that cannot be equal on
two machines and therefore reported a desync on every single step.

- Global.simulation_tick: the authority counts the steps, a client takes the
  number from the authoritative frame it is executing. FRAME_INFO and the
  command requests carry it, so the network timeline has one unambiguous order
  independent of frame rates
- network/statehash: a versioned FNV digest of the gameplay critical state -
  vehicle position, speed, direction, brake pipe and cylinder pressure, plus the
  clock. Values are quantized so that float noise is not mistaken for drift, and
  vehicles are folded in under their own identity and summed, so the digest does
  not depend on the order the scenario created them in. Switches, memcells and
  the event queue are the next candidates; they go in when stage 7 pins down
  what a snapshot contains
- a client compares its own digest against the authoritative one and reports the
  drift when it starts and then only now and then, rather than every step; the
  overlay says how long it has been out of step and at which tick
- refusing to compare at all when the two builds disagree on the digest layout

EU07_NETWORK_VERSION is now 4.
2026-09-06 19:21:12 +00:00
4870935983 multiplayer stage 5: the authority decides which events fire
Every peer used to evaluate event conditions for itself and put the results
straight into its own queue, so the same event could fire twice, or on one
machine only. Worse, the launchers were polled around Global.pCamera, which in
a session means "wherever one particular player happens to be looking".

Condition evaluation is now separated from execution:

    authority:  evaluate -> queueevent -> execute -> replicate
    client:     receive queueevent -> execute

- AddToQuery() takes the origin of the request. A locally evaluated launch on a
  client is dropped, on the host it is handed to the session as the ordinary
  queueevent command and comes back to be carried out by everybody, itself
  included, so it happens exactly once per peer. Scenario init (onstart,
  negative delay) and the children of an already replicated event are marked as
  authority decisions and run in place, since they are identical everywhere.
  With no session running, both branches collapse to the old behaviour
- queueevent now carries the delay as well
- event launchers are polled around every crewed vehicle instead of around the
  local camera, and a cell is polled at most once per frame no matter how many
  players stand near it - launcher activation counters are stateful
- Events.update() still services the queue on a client, with its delays and its
  execution; only the condition checking is skipped there

An event whose request is still travelling is not requested again, with a
timeout so that a lost request cannot silence it for good. Instant (AddValues)
events are exempt from that guard, because every one of their additions has to
get through.
2026-09-06 19:09:19 +00:00
c3bf261934 multiplayer stage 4: server-authoritative input, permissions, per-source holds
Three things stood in the way of two people sharing a cab and of a client not
being able to reach past its own vehicle.

Vehicle commands were addressed by TTrain::id(), which every peer hands out
locally from its own counter, so the same number means a different vehicle on
a different peer. Commands now travel with the session wide NetworkEntityId
and are translated back to the local cab on arrival; outside of multiplayer
the registry is empty and both directions are a no-op, so singleplayer is
untouched.

A client could send any command it liked, including setweather, queueevent or
spawntrainset. The server now validates every incoming command: a player may
work the controls of the vehicle whose crew they are on and nothing else, and
world wide commands stay with the host until dispatcher roles exist. The
source is stamped by the server, never taken from what the peer sent, and
rejections are logged once per peer/command instead of every frame. Host input
runs through the very same validation, just without the transport.

Continuous commands were tracked per command and recipient only, so with two
people in one cab the release sent by one of them cancelled the other one's
hold. command_data now carries the source peer and the active set is keyed by
{source, recipient, command}.

Also: a client asking to quit no longer replicates quitsimulation, which would
have shut down everybody else's session.
2026-09-06 18:24:21 +00:00
3f62fd5927 multiplayer stage 3: crew model, claim handshake and AI handover
A vehicle is not owned by one player: two people share a cab, so the session
needs a crew relation rather than an owner field. The server keeps it and is
the only one allowed to change it.

- network/session: vehicle_crew { entity_id, crew, ai_before_claim } and a
  crew_registry holding the roster. the host goes through exactly the same
  path as a remote peer (claim -> crew -> grant), only without transport
- CLAIM_VEHICLE / CLAIM_GRANTED / CLAIM_DENIED / LEAVE_VEHICLE / CREW_UPDATE;
  the server validates the vehicle exists, is present in the scenario and has
  room in the crew, and answers a refusal with a reason the lobby shows
- the cab of a claimed vehicle is created by the authority posting the
  ordinary replicated entervehicle, so every peer builds it and the vehicle is
  never moved. the claiming peer only walks in once the cab appears locally
- AI transitions follow the crew size: 0 -> 1 remembers whether an AI was
  really driving and takes the vehicle over, 1 -> 2 and 2 -> 1 change nothing,
  1 -> 0 hands the vehicle back but only if there was an AI driver to begin
  with. driver mode no longer fires aidriverdisable of its own in multiplayer
- losing a connection removes that peer from every crew it was on; a vehicle
  with somebody still on board carries on untouched
- the lobby lists who is on board, offers "join crew" for a vehicle another
  player already occupies, and lets a player step off

The AI and cab handling runs as a reconciliation pass rather than as edge
triggered actions, so a request that has not taken effect yet (the cab needs a
frame to appear) is simply repeated, with a cooldown to keep it off the wire.
2026-09-06 18:08:06 +00:00
b9bd0ee33d multiplayer stage 2: peer identity, stable vehicle ids and a session lobby
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.
2026-09-06 17:48:35 +00:00
8460fc531f multiplayer stage 1: --host/--connect startup, client without a local scenario
Networking used to be reachable only through the eu07.ini network.server /
network.client entries, and init_modes() refused to start at all unless a
scenery file was already known. That made it impossible to join a session
without knowing (and typing) the host's scenario up front, even though
SERVER_HELLO already carries the scenario name.

- add --host [address:port] and --connect address:port to the command line
  (default port 7420, --host defaults to 0.0.0.0), plus --scenario/--vehicle
  aliases and -h/--help; the existing ini settings keep working
- drop the init_modes() restriction: a client goes straight to the scenario
  loader and waits there for the handshake, a host without -s still gets the
  launcher and simply refuses joins until a scenario is up
- the loader screen reports the network state instead of sitting on a blank
  progress bar
- a client that did not ask for a vehicle with -v starts as an observer
  rather than failing to find the default EU07-424; the lobby of stage 2 will
  take over the choice
- handshake carries build identification and the server answers an
  impossible join with SERVER_REJECT and a readable reason instead of
  dropping the socket silently; protocol version bumped to 3
- deserialize_message() no longer dereferences a null message on an unknown
  message type
2026-09-06 17:28:44 +00:00
eed44dc858 hardware protocol v2.1: device log, diagnostic routines, manual links
Three additions, all driven from the simulator debug panel.

DEVICE_LOG lets a controller send text lines; they reach the simulator log
and a per connection view in the panel which keeps the last 64 of them.

Diagnostic routines: a controller lists the procedures it offers, the
operator picks one from a drop down and starts it, and the controller
reports progress and the result. While a routine runs the controller can
ask the simulator to show a message box with its own text and buttons and
gets back which one was pressed, which is what a valve or gauge
calibration needs. The message box appears whether the debug panel is
open or not.

Links can now be disconnected and reconnected by hand: disconnecting
closes the serial port so the controller can be reprogrammed, and the
session is rebuilt from scratch on reconnect. Ports which appear while
the simulation runs can be picked from a refreshed list, given a baud
rate and added as a new controller, so the manager now starts even with
no uart2 entries in eu07.ini.
2026-09-06 11:29:26 +00:00
da962cd91b Add static cast 2026-09-06 01:34:25 +02:00
c5825db208 Change float64 to uint32 2026-09-06 01:22:57 +02:00
be48b6f013 Add engine voltage parameter 2026-09-06 00:59:46 +02:00
40df8e84f6 zmq: read simulator state from the shared TrainState registry
The zmq interface kept its own copy of the state field list, its own name
table and its own packer. All three are now the shared registry, which
resolves the same channel names and returns the same values, so the wire
format is unchanged. New state exposed to external interfaces is added in
one place from now on.

The registry reports the raw velocity rather than zeroing it while the
simulation is paused, and a symbol which doesn't apply to the current
vehicle keeps its measurement and says so through its quality flag.
2026-09-05 20:41:59 +00:00
6bfd21d135 hardware protocol v2: answer a subscription with current state
The snapshot sent right after SUBSCRIBE was taken from the previous
frame, which on the first frame of a session meant every value came back
as UNAVAILABLE. The link now publishes the state of the current frame
before the received frames are processed.
2026-09-05 20:40:14 +00:00
91be90548f hardware protocol v2: configuration, wiring and debug panel
Adds the WITH_HARDWARE_PROTOCOL_V2 build option and the eu07.ini entries
which configure it: uart2 declares a serial link and may be repeated for
several controllers, the remaining uart2* entries tune frame size,
heartbeat, rate limits and logging. UART v1 is untouched and both may be
active at the same time on different ports.

The driver mode starts the manager when at least one link is configured
and pumps it from the input polling loop, and the debug panel gains a
Hardware Protocol v2 section reporting link and session condition,
packet rates, round trip time, protocol and transport error counters and
whatever diagnostics the device itself reports, with switches for
protocol and frame logging.
2026-09-05 20:35:38 +00:00
4e3afdd2a6 hardware protocol v2: sessions, subscriptions and the transport worker
Adds the logical protocol layer on top of the frame codec: session
handshake with negotiated frame size, session scoped symbol handles,
state subscriptions with periodic, on change and deadband filtering,
command events with duplicate protection and continuous controls with
stale update rejection, plus per connection rate limits and diagnostics.

A single worker thread owns all of the blocking transport work and
exchanges finished frames with the simulation thread through short locked
queues, so a stalled, flooding or unplugged controller cannot hold up the
simulation.
2026-09-05 20:30:32 +00:00
7cb6f789f5 hardware protocol v2: frame layer and shared symbol registry
Adds the transport-independent core of the MaSzyna Hardware Protocol v2:
COBS framing with a CRC32C protected 24 byte header, bounds checked
payload readers and writers, and a resynchronising stream decoder.

The shared TrainState registry describes simulator state in engineering
units with per-value quality, the command registry generates the command
namespace from Commands_descriptions, and the symbol registry merges both
into the public, name-addressed hardware api.
2026-09-05 20:22:08 +00:00
7a469190ca Merge pull request #168 from pwarszawski/whois-owner-fallback
whois: fall back to the consist owner in the +0/+7 mode too
2026-09-05 21:08:29 +02:00
3b061a1bf1 Merge pull request #171 from pwarszawski/event-name-registration-casefold
event names: fix registration/lookup casing mismatch for non-ASCII identifiers
2026-09-05 21:07:41 +02:00
76eede79aa Merge pull request #154 from MaSzyna-EU07/uart-independent-dynamic-brake
Add independent dynamic brake support (eg. Vectron) to UART controllers
2026-09-05 20:04:34 +02:00
fadcc2d666 Merge pull request #170 from MaSzyna-EU07/fix/reflections-and-burned-colors
Fix relfections on default shader and tonemapping
2026-09-05 20:03:31 +02:00
Piotr Warszawski
2dcae05c77 event names: drop redundant locale-dependent ToLower() at registration
make_event() lowercased the event name twice: cParser::getToken() already
folds ASCII via toLowerChar() (which intentionally leaves bytes >= 0x80
alone), then ToLower() ran std::tolower() over every byte on top of that.

With a non-"C" global locale, that second pass remaps cp1250 uppercase
accented letters (e.g. 0xAF -> 0xBF). Every lookup path - TTrack::AssignEvents(),
child-event resolution in multi_event, event-launcher name resolution -
normalizes only through the parser, so a registered name containing such a
letter can no longer be found: the track logs "can't find assigned event"
and the binding is silently dropped.

Removing the extra ToLower() makes the registration key match every lookup
site (ASCII-only folding, non-ASCII bytes compared verbatim), and matches
the direction toLowerChar()'s own comment already points at for UTF-8.
2026-09-05 19:10:58 +02:00
Piotr Warszawski
1ce576c0c6 whois: log the no-activator case instead of failing silently
A whois with no triggering vehicle previously just wrote nothing to
the target cell with no trace in the log, which is a real pain to
diagnose from the scenery side (this is exactly the kind of thing that
cost real debugging time on a station script recently).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 22:22:25 +02:00
Piotr Warszawski
7ead4f11b4 whois: guard against a null activator
whois_event::run_() dereferenced m_activator unconditionally in every
branch (this predates this PR). getvalues_event::run_() and
putvalues_event::run_() already guard the same way elsewhere in this
file; whois_event was missing it. Single early return covers every
branch, old and new.
2026-09-04 22:09:23 +02:00
Piotr Warszawski
4f47233a89 whois: fall back to the consist owner in the +0/+7 mode too
The +8/+16/+24/+32/+40 (and the new +48) modes all pick the reporting
vehicle the same way: the activator's own Mechanik if it's the primary
driver, otherwise the consist's controlling vehicle (ctOwner) - so the
event still resolves correctly if a wagon happens to trigger it ahead
of its locomotive.

+0 (train name / stations left / stop flag) predates that pattern and
never got it: it required m_activator->Mechanik directly and silently
wrote nothing otherwise. A whois hook placed where the loco doesn't
necessarily lead (e.g. a train that reverses direction, or one being
propelled) can have a wagon trigger the event first and the memcell
stays empty, with no error - hard to diagnose from scenery side.

Same owner-selection, same guard semantics (still nothing written if
neither the activator nor its consist has a controller), only the
source of the vehicle it reads from is widened.
2026-09-04 21:01:33 +02:00
6befd69968 Merge pull request #149 from combolek/getpf
state_manager: Optimize computation of pressure flow for Oerlikon ESt valves
2026-09-04 16:39:49 +02:00
maj00r
a9b561cd65 Merge pull request #162 from MaSzyna-EU07/bugfix/260825-00jor
make targetfps great again
2026-09-03 22:18:16 +02:00
jakubg1
db6296124b Merge pull request #166 from jakubg1/dev/revert_remove_instructor_panel
Revert "Remove training card window"
2026-09-02 12:37:40 +02:00
jakubg1
ba19db8715 Revert "Remove training card window"
This reverts commit 363b50c9c2.
2026-09-02 12:32:35 +02:00
jakubg1
4e35a5f45f Merge pull request #160 from jakubg1/dev/remove_instructor_panel
Remove training card window
2026-09-02 11:59:49 +02:00
jakubg1
a2f62c56ff Merge pull request #161 from jakubg1/dev/epfuse_engage_fix
Fix EP fuse auto-(dis)engaging
2026-09-02 11:59:30 +02:00
766a957527 Fix relfections on default shader and tonemapping 2026-09-01 20:41:06 +02:00
maj00r
0aceb120e2 make targetfps great again 2026-08-25 18:03:55 +02:00
jakubg1
f8cb5e4431 Fix EP fuse auto-(dis)engaging
When the Autorewident function is enabled, the EP fuse switch will no longer automatically engage or disengage if the player is controlling the train. This would happen for example when approaching a station.
2026-08-24 00:24:15 +02:00
jakubg1
363b50c9c2 Remove training card window
This was a leftover from attempts at modifying MaSzyna for professional simulators.
Interaction with this window could cause a crash.
2026-08-23 22:37:33 +02:00
jakubg1
9a0b63a97c Merge pull request #133 from dos1/launcher-panel
launcher: Open on scenery list panel
2026-08-23 21:53:12 +02:00
df9ba73a88 Merge pull request #158 from MaSzyna-EU07/migrate-to-actions
Migrate to actions
2026-08-20 23:11:27 +02:00
66302bd88e Remove appveyor and update readme 2026-08-19 14:50:28 +02:00
8024c2be7b Revert "Delete appveyor.yml"
This reverts commit 8a22553e7c.
2026-08-19 14:48:03 +02:00
3c61168783 Revert "Delete azure-pipelines.yml"
This reverts commit 67bb6e1d3c.
2026-08-19 14:47:59 +02:00
67bb6e1d3c Delete azure-pipelines.yml eu07-2026.423-prerelease 2026-08-19 14:45:49 +02:00
8a22553e7c Delete appveyor.yml eu07-2026.422-prerelease 2026-08-19 14:45:33 +02:00