Compare commits
30 Commits
Author | SHA1 | Date |
---|---|---|
Florian Obser | 5275adb20c | |
Florian Obser | f29d88e845 | |
Florian Obser | b510145d24 | |
Florian Obser | 3ee4c6146c | |
Florian Obser | 4298004b30 | |
Florian Obser | 46d247c0e6 | |
Florian Obser | b697286ade | |
Florian Obser | d0b14e6b30 | |
Florian Obser | 6431d175c1 | |
Florian Obser | 95132894e3 | |
Florian Obser | 20b5971137 | |
Florian Obser | e2f975ef05 | |
Florian Obser | d3f110941a | |
Florian Obser | de091f557b | |
Florian Obser | 8d2d3a4d3a | |
Florian Obser | 3c63ee0b52 | |
Florian Obser | a8efc6b2e8 | |
Florian Obser | 1a0d2846ab | |
Florian Obser | d30ba8ca8e | |
Florian Obser | 62865f9bcf | |
Florian Obser | ee4976b816 | |
Florian Obser | 44ae2024d1 | |
Florian Obser | 8f4064baee | |
Florian Obser | afd22dd2e4 | |
Florian Obser | 14f17c6b04 | |
Florian Obser | 233abcdee2 | |
Florian Obser | f294f171b5 | |
Florian Obser | 066f90e34b | |
Florian Obser | c589c89f60 | |
Florian Obser | e5933409ef |
|
@ -0,0 +1,123 @@
|
|||
#+TITLE: SingleFile
|
||||
#+DATE: 2024-03-20
|
||||
* Prologue
|
||||
I am using the [[https://en.wikipedia.org/wiki/Zettelkasten#Use_in_personal_knowledge_management][Zettelkasten]] methodology for personal knowledge
|
||||
management, with [[https://www.orgroam.com/][Org-roam]] to implement it.
|
||||
|
||||
While the Internet does not forget things in general, it might be
|
||||
difficult to find things again. Maybe someone published some
|
||||
information in a GitHub gist or has their whole blog there.
|
||||
Suddenly GitHub falls out of favour because it got bought by an
|
||||
evilcorp and information disappears.
|
||||
|
||||
For those reasons I store a link to the information directly, a link
|
||||
to the [[https://web.archive.org/][Wayback Machine]] and a local copy. My Zettelkasten and all
|
||||
references, including a local copy, are stored in git.
|
||||
|
||||
* SingleFile
|
||||
[[https://github.com/gildas-lormeau/SingleFile][SingleFile]] is a Firefox[fn::Other browsers are supported as well, I
|
||||
just think Firefox is the least-evil one.] extension to save a website,
|
||||
including images and css, into a single HTML file. It's perfect for
|
||||
personal archival purposes. When run as a plugin it will save the page
|
||||
as it is currently rendered. For example dismissed cookie banners will
|
||||
not be part of the safes page.
|
||||
|
||||
The downside is that it's some semi-manual process to get the backup
|
||||
into the Zettelkasten note.
|
||||
|
||||
* SingleFile-CLI
|
||||
There is also a [[https://github.com/gildas-lormeau/single-file-cli][CLI]] version, like all modern stuff it runs in
|
||||
docker. Of course I do not have docker on OpenBSD, nor the alternative
|
||||
podman.
|
||||
|
||||
So I setup a Fedora VM, installed podman and run singlefile-cli via
|
||||
ssh. On the Fedora VM I use this wrapper:
|
||||
#+begin_src sh
|
||||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
content=$(podman run --privileged -u 0:0 singlefile "$@")
|
||||
|
||||
now=$(date --iso-8601=seconds -u)
|
||||
title=$(echo ${content} | /home/florian/.cargo/bin/htmlq --text title | \
|
||||
awk '{$1=$1};1' | \
|
||||
head -1 | tr -d '\n' | tr -c '[:alnum:]' '_' | cut -c -128)
|
||||
|
||||
fn="/tmp/${title}_${now}.html"
|
||||
|
||||
echo ${content} > ${fn}
|
||||
echo $fn
|
||||
#+end_src
|
||||
|
||||
It fetches a copy of the website, uses [[https://github.com/mgdm/htmlq][htmlq]] to extract the title,
|
||||
removes special characters and saves the page in ~/tmp~. It then spits
|
||||
out the file-name of the backup.
|
||||
|
||||
On my laptop I have this script:
|
||||
#+begin_src sh
|
||||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
fn=$(ssh fedora bin/singlefile "$@")
|
||||
bn=$(basename ${fn})
|
||||
scp fedora:${fn} .
|
||||
open ${bn}
|
||||
#+end_src
|
||||
This causes the Fedora VM to fetch a backup of the website and then
|
||||
copies it over and stores it in the current working directory. Finally
|
||||
it opens it in the browser for inspection.
|
||||
|
||||
* org-cliplink
|
||||
We still do not have links stored in the Zettelkasten note.
|
||||
|
||||
For that I use [[https://github.com/rexim/org-cliplink][org-cliplink]] to insert org mode links from the
|
||||
clipboard.
|
||||
|
||||
A bit of elisp code massages the link text to add "Archive: " or
|
||||
"Local: " in front of the title. It also changes the URL for the local
|
||||
backup to have it relative so that it works correctly no matter where
|
||||
the git repo with the Zettelkasten is checked out.
|
||||
|
||||
#+begin_src elisp
|
||||
(use-package org-cliplink
|
||||
:config
|
||||
(defun custom-org-cliplink ()
|
||||
(interactive)
|
||||
(org-cliplink-insert-transformed-title
|
||||
(org-cliplink-clipboard-content) ;take the URL from the CLIPBOARD
|
||||
(lambda (url title)
|
||||
(let* ((parsed-url (url-generic-parse-url url)) ;parse the url
|
||||
(turl
|
||||
(cond
|
||||
;; if type is file make the url relative to zettelkasten
|
||||
((string= (url-type parsed-url) "file")
|
||||
(url-unhex-string (replace-regexp-in-string "\\(.+\\)\/zettelkasten\/\\(.+\\)" "file:\\2" url)))
|
||||
;; otherwise keep the original url
|
||||
(t url)))
|
||||
(ttitle
|
||||
(cond
|
||||
;; if type is file, add Local: to title
|
||||
((string= (url-type parsed-url) "file")
|
||||
(replace-regexp-in-string "\\(.+\\)" "Local: \\1" title))
|
||||
;; otherwise keep the original title
|
||||
(t title))))
|
||||
;; forward to the default org-cliplink transformer
|
||||
(org-cliplink-org-mode-link-transformer turl ttitle)))))
|
||||
:custom
|
||||
(org-cliplink-title-replacements
|
||||
'(("https://github.com/.+/?"
|
||||
("\\(.*\\) · \\(?:Issue\\|Pull Request\\) #\\([0-9]+\\) · \\(.*\\) · GitHub" "\\3#\\2 \\1"))
|
||||
("https://twitter.com/.+/status/[[:digit:]]+/?"
|
||||
(".+ on Twitter: \\(.+\\)" "\\1"))
|
||||
("https://web.archive.org/.+/?"
|
||||
("\\(.+\\)" "Archive: \\1"))))
|
||||
:bind (:map org-mode-map
|
||||
("C-c C-S-L" . custom-org-cliplink)))
|
||||
#+end_src
|
||||
|
||||
* Epilogue
|
||||
I only recently added singlefile-cli to my work-flow, so far it is a
|
||||
big improvement. Time will tell if the backups are cluttered with
|
||||
banners and I have to go back to semi-manual mode.
|
|
@ -591,7 +591,7 @@ We set it to a very low priority[fn:: Remember, a high priority
|
|||
dhcpleased(8) configures when we move to an IPv4 enabled network.
|
||||
|
||||
We then need to configure address family translation in pf(4) when we
|
||||
detect /NAT64/ being present. This is were [[https://github.com/fobser/gelatod/][gelatod(8)]] comes in. It is
|
||||
detect /NAT64/ being present. This is were [[https://codeberg.org/fobser/gelatod][gelatod(8)]] comes in. It is
|
||||
a Customer-side transLATor (/CLAT/) configuration daemon[fn::If you
|
||||
squint just right, gelato kinda sounds like clat[fn::Again, I really
|
||||
really should be prohibited from naming things.].]. /CLAT/ is what
|
||||
|
|
|
@ -50,8 +50,8 @@ comes down to opening the correct file. So we can copy the code of
|
|||
(if (listp value)
|
||||
(mapcar 'pop-to-buffer-same-window (nreverse value))
|
||||
(pop-to-buffer-same-window value))
|
||||
(read-only-mode)
|
||||
(text-mode)))
|
||||
(text-mode)
|
||||
(read-only-mode)))
|
||||
|
||||
;; RFCs are stored in 'in-notes/'
|
||||
(defun ietf-rfc (filename &optional wildcards)
|
||||
|
@ -66,10 +66,15 @@ comes down to opening the correct file. So we can copy the code of
|
|||
(if (listp value)
|
||||
(mapcar 'pop-to-buffer-same-window (nreverse value))
|
||||
(pop-to-buffer-same-window value))
|
||||
(read-only-mode)
|
||||
(text-mode)))
|
||||
(text-mode)
|
||||
(read-only-mode)))
|
||||
#+end_src
|
||||
|
||||
*Update 2024-03-25*: Flipping the order of ~(text-mode)~ and
|
||||
~(read-only-mode)~ so that ~(read-only-mode)~ gets activated last
|
||||
makes it work correctly with ~view-mode~ if that mode is automatically
|
||||
activated by ~(setq view-read-only t)~ on read-only buffers.
|
||||
|
||||
Setting a global key binding lets us open RFCs and drafts from
|
||||
anywhere within Emacs:
|
||||
#+begin_src emacs-lisp
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
+ [[https://www.linkedin.com/in/florian-obser-75900383][Linkedin]]
|
||||
|
||||
* Meditations
|
||||
- [[file:SingleFile.org][2024-03-20: SingleFile]]
|
||||
- [[file:openttd-srnw.org][2024-01-13: OpenTTD Self Regulating Networks]]
|
||||
- [[file:mastodon-backup.org][2023-07-26: Mastodon Backup]]
|
||||
- [[file:emacs-ietf.org][2023-04-05: Emacs IETF]]
|
||||
- [[file:dynamic_host_configuration_please.org][2023-03-07: Dynamic host configuration, please]]
|
||||
- [[file:privsep.org][2023-02-19: Privilege drop, privilege separation, and restricted-service operating mode in OpenBSD]]
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
#+TITLE: Mastodon Backup
|
||||
#+DATE: 2023-07-26
|
||||
* Prologue
|
||||
People on the Fediverse like to point out how you have to backup your
|
||||
data regularly in case of a catastrophic failure effecting your
|
||||
instance, the host of your instance, the data centre of your instance
|
||||
or maybe your admin. Heck, even a catastrophic failure effecting an
|
||||
admin of /another/ instance might make you wish you had a backup of
|
||||
your follows list[fn::If a remote admin decides to de-federate your
|
||||
instance it will sever all your connections with people on that
|
||||
instance.].
|
||||
|
||||
Remembering to make a backup manually is already difficult
|
||||
enough[fn::My last manual backup was over a year old.], but then you
|
||||
also need to remember *how* to make that backup...
|
||||
* Manual Backups
|
||||
Got to "App Settings". You do not know where "App Settings" are? I
|
||||
know the feeling... They are behind the three cogs on the top
|
||||
left. Navigate to "Import and export". Click "Data export". Click the
|
||||
"CSV" link. Click the other "CSV" link. Click the other other "CSV"
|
||||
link. You have now downloaded your "follows", "muted" and "blocked"
|
||||
accounts lists.
|
||||
* Automatic Backups
|
||||
I have recently discovered [[https://toot.bezdomni.net/introduction.html][toot - Mastodon CLI client]] which can output
|
||||
a list of your follows and people following you. I have [[https://github.com/ihabunek/toot/pull/390][contributed
|
||||
code for "muted" and "blocked" commands]] which got accepted and
|
||||
released in version 0.38.
|
||||
|
||||
We first need to authenticate to our instance by running ~toot
|
||||
login~. This is only needed once. For more information [[https://toot.bezdomni.net/usage.html][see the
|
||||
official documentation]].
|
||||
|
||||
I am running the following script from cron once a day:
|
||||
#+begin_src shell
|
||||
#! /bin/ksh
|
||||
ACCOUNT=@florian@bsd.network
|
||||
INSTANCE=bsd.network
|
||||
BACKUPDIR=/home/mastodonbackup/backup
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
exit_if_nonzero_or_stderr() {
|
||||
(
|
||||
set -o pipefail
|
||||
{ "$@" 1>&3 ;} 2>&1 | {
|
||||
if IFS= read -r line; then
|
||||
printf "%s\n" "$line"
|
||||
cat
|
||||
exit 1
|
||||
fi
|
||||
} >&2
|
||||
) 3>&1
|
||||
}
|
||||
|
||||
exit_if_nonzero_or_stderr /usr/local/bin/toot muted | \
|
||||
awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
|
||||
> ${BACKUPDIR}/muted.new
|
||||
|
||||
mv ${BACKUPDIR}/muted{.new,}
|
||||
|
||||
exit_if_nonzero_or_stderr /usr/local/bin/toot blocked | \
|
||||
awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
|
||||
> ${BACKUPDIR}/blocked.new
|
||||
mv ${BACKUPDIR}/blocked{.new,}
|
||||
|
||||
exit_if_nonzero_or_stderr /usr/local/bin/toot following ${ACCOUNT} | \
|
||||
awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
|
||||
> ${BACKUPDIR}/following.new
|
||||
mv ${BACKUPDIR}/following{.new,}
|
||||
|
||||
exit_if_nonzero_or_stderr /usr/local/bin/toot followers ${ACCOUNT} | \
|
||||
awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
|
||||
> ${BACKUPDIR}/followers.new
|
||||
mv ${BACKUPDIR}/followers{.new,}
|
||||
#+end_src
|
||||
|
||||
~toot~ outputs accounts from the local instance without
|
||||
/@instance/. ~awk(1)~ checks for the existence of two /@/ characters
|
||||
and if they are not present outputs /@instance@/ after the account
|
||||
name.
|
||||
|
||||
Unfortunately ~toot~ always exits with 0, even if there is an error so
|
||||
we need to check if there is some output on =stderr= so that we do not
|
||||
replace our backup with empty files. For that I copy-pasted
|
||||
=exit_if_nonzero_or_stderr= from [[https://stackoverflow.com/a/61468182][stackoverflow]] like a pro.
|
||||
|
||||
My crontab entry looks like this:
|
||||
#+begin_src crontab
|
||||
~ 9 * * * -sn su -s /bin/sh mastodonbackup -c /home/mastodonbackup/backup.sh
|
||||
#+end_src
|
||||
=-sn= ensures that the script is only run once and that no mail is
|
||||
sent after a successful run. See [[https://man.openbsd.org/crontab.5][crontab(5)]] for details.
|
||||
|
||||
* Epilogue
|
||||
I have automated backing up the most important lists from my mastodon
|
||||
account. My mastodon account is running on somebody else's
|
||||
computer. The backup data is written as text files on a system that I
|
||||
control. That system is hooked up to my standard backup solution to
|
||||
have daily, multiple-redundant backups.
|
||||
|
||||
I expire my toots after 90 days using [[https://ephemetoot.hugh.run/][ephemetoot]]. I have configured it
|
||||
to save the toots and media before deleting them from my account.
|
|
@ -28,9 +28,12 @@ I'll just leave this here...
|
|||
|
||||
- name: reboot and wait for host to return
|
||||
block:
|
||||
- name: schedule reboot in 1 minute
|
||||
- name: reboot
|
||||
ansible.builtin.command:
|
||||
cmd: 'shutdown -r +1'
|
||||
cmd: reboot
|
||||
ignore_errors: yes
|
||||
async: 3600
|
||||
poll: 0
|
||||
- name: wait for ssh to go away
|
||||
ansible.builtin.wait_for:
|
||||
host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}'
|
||||
|
|
|
@ -8,13 +8,13 @@ upstream.
|
|||
git clone from the upstream repository on [[https://github.com/NLnetLabs/nsd][github]].
|
||||
#+begin_src shell
|
||||
git pull
|
||||
git diff NSD_4_3_6_REL..NSD_4_3_7_REL . ':!.cirrus.yml' ':!tpkg/*' \
|
||||
':!contrib/*' ':!compat/' ':!README.md' ':!.gitignore' \
|
||||
':!.buildkite/*' ':!makedist.sh' > ~/nsd_4.3.7_upstream.diff
|
||||
git diff NSD_4_6_1_REL..NSD_4_7_0_REL . ':!.cirrus.yml' ':!tpkg/*' \
|
||||
':!contrib/*' ':!compat/' ':!.gitignore' ':!doc/README.svn' \
|
||||
':!doc/CREDITS' \
|
||||
':!.buildkite/*' ':!makedist.sh' > ~/nsd_4.7.0_upstream.diff
|
||||
cd /usr/src/usr.sbin/nsd
|
||||
patch -Ep0 < ~/nsd_4.3.7_upstream.diff
|
||||
autoheader-2.69
|
||||
autoconf-2.69
|
||||
patch -Ep0 < ~/nsd_4.7.0_upstream.diff
|
||||
autoconf-2.71
|
||||
make -f Makefile.bsd-wrapper obj
|
||||
make -f Makefile.bsd-wrapper clean
|
||||
make -f Makefile.bsd-wrapper -j4
|
||||
|
@ -22,7 +22,7 @@ git clone from the upstream repository on [[https://github.com/NLnetLabs/nsd][gi
|
|||
We also need to update the version numbers in the man pages. For that
|
||||
we download the release tar ball and generate a diff:
|
||||
#+begin_src shell
|
||||
diff -ru . /home/florian/nsd-4.3.7/ | fgrep -v Only > sync.diff
|
||||
diff -bru . /home/florian/nsd-4.7.0/ | fgrep -v Only > sync.diff
|
||||
#+end_src
|
||||
The diff then needs to be partially applied, some changes are
|
||||
intentional.
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
#+TITLE: OpenTTD Self Regulating Networks
|
||||
#+DATE: 2024-01-13
|
||||
[[file:openttd-srnw/srnw_head.png]]
|
||||
* Prologue
|
||||
Every other year or so I decide to play a game or ten of
|
||||
[[https://openttd.org/][OpenTTD]]. Unfortunately I don't remember most of the things I figured
|
||||
out in the past so I have to start from first principles.
|
||||
In this case, first principles means reading the [[https://wiki.openttdcoop.org/Main_Page][openttdcoop Wiki]]
|
||||
[fn::which is quite dense] and watching a bunch of [[https://www.youtube.com/@LugnutsK][LugnutsK]]
|
||||
videos. For the topic at hand I would suggest
|
||||
[[https://www.youtube.com/watch?v=ZVtszocBmiY][Advanced OpenTTD - Self-Regulating Networks]].
|
||||
|
||||
* Self Regulating Passenger Network
|
||||
The idea of a self regulating passenger network is to grow a city and
|
||||
transport as many passengers out of the city as possible with the
|
||||
least amount of train orders.
|
||||
|
||||
openttdcoop calls it [[http://wiki.openttdcoop.org/Gametype:ICE_SBahn][Gametype:ICE SBahn]] / [[http://wiki.openttdcoop.org/Self-regulating_SBahn][Self-regulating SBahn]].
|
||||
|
||||
The idea is to have (SBahn) stations spread throughout the city that
|
||||
collect passengers. Once a train is fully loaded with passengers, it
|
||||
drives outside of the city to a transfer (ICE) station and transfers
|
||||
passengers to a waiting train that then transports the passengers to a
|
||||
far away city to make money.
|
||||
|
||||
We do not want to deal with each station individually but run the
|
||||
pick-up trains in a loop that automatically visits a station with
|
||||
a full load of passengers available.
|
||||
|
||||
To make all of this work we have:
|
||||
1. Inner-city SBahn stations
|
||||
2. Outside-city waiting loop for the pick-up trains
|
||||
3. Outside-city exit track to the transfer / ICE station
|
||||
4. Outside-city transfer / ICE station
|
||||
5. Injection mechanism from the ICE station to the waiting loop.
|
||||
|
||||
[[file:openttd-srnw/overview.png][file:openttd-srnw/overview_small.png]]
|
||||
|
||||
The pick-up trains have two orders:
|
||||
1. Go to transfer / ICE station and transfer passengers.
|
||||
2. Go to inject way-point.
|
||||
|
||||
The inject way-point leads to the waiting loop. From there the only
|
||||
way to reach the transfer station is through any of the SBahn
|
||||
stations. We have to arrange the SBahn stations in a way that they are
|
||||
only accessible when there is a full load of passengers available.
|
||||
|
||||
* City Layout
|
||||
For maximum growth and ease of putting train stations into the city we
|
||||
are using a 3x3 road grid. A spiral would be even better for city
|
||||
growth but it is annoying to put train stations into a spiral without
|
||||
disrupting the roads too much and creating dead-ends. Dead-ends are
|
||||
very bad for town growth.
|
||||
|
||||
We need to find a balance between station size[fn::One constraint here
|
||||
is how much space the station takes away from the city to build houses.],
|
||||
city coverage, and needed capacity at the transfer station. Using spread
|
||||
stations covering a 5x5 block[fn::A block is a 3x3 road grid.] and
|
||||
having six of those works fairly well. To be able to fit tunnels in we
|
||||
put two in a line and have three lines[fn::Three in a line would also
|
||||
leave enough space for tunnels. But then the waiting loop might become
|
||||
too big and it takes to long to try all the SBahn stations. This is
|
||||
especially a problem once the city is big and produces lots of
|
||||
passengers.]. This results in a city grid of 13x8 blocks.
|
||||
|
||||
[[file:openttd-srnw/city-layout.png][file:openttd-srnw/city-layout_small.png]]
|
||||
|
||||
* Station Construction
|
||||
We are building a spread station in a 3x3 block by putting a city
|
||||
station in each of the corners of the block. Holding =ctrl= while
|
||||
placing the building opens the "Join station" menu and we select an
|
||||
existing station.
|
||||
|
||||
[[file:openttd-srnw/spread-station.png][file:openttd-srnw/spread-station_small.png]]
|
||||
|
||||
The coverage of the station is not perfect but some of these parts
|
||||
will later be filled by the tracks of the train station. Note that the
|
||||
station also covers a block outside of the road grid, so each spread
|
||||
station covers a 5x5 block. This is important when putting in adjacent
|
||||
stations to not have them overlap.
|
||||
|
||||
[[file:openttd-srnw/station-coverage.png][file:openttd-srnw/station-coverage_small.png]]
|
||||
|
||||
For the rail network we dig two trenches outside of the city. Tunnels
|
||||
to those will connect the station to the waiting loop on one side of
|
||||
the city and the exit to the transfer station on the other side of the
|
||||
city.
|
||||
|
||||
We then remove some roads and the crossings[fn::This ensures that we
|
||||
can dig a complete hole, we will put the roads back in later.] on one
|
||||
side of the block and dig a 7x2 hole for the train tracks.
|
||||
|
||||
[[file:openttd-srnw/station-digging.png][file:openttd-srnw/station-digging_small.png]]
|
||||
|
||||
Next: Tunnels, tracks, signal, a way-point, and a train depot.
|
||||
|
||||
[[file:openttd-srnw/station-tunnels-and-tracks.png][file:openttd-srnw/station-tunnels-and-tracks_small.png]]
|
||||
|
||||
We then place a two platform, length five station, making sure we join
|
||||
it with the existing spread station.
|
||||
|
||||
[[file:openttd-srnw/station-placing-tracks.png][file:openttd-srnw/station-placing-tracks_small.png]]
|
||||
|
||||
The finished station looks like this:
|
||||
|
||||
[[file:openttd-srnw/station-anotation.png][file:openttd-srnw/station-anotation_small.png]]
|
||||
|
||||
We are going to put a dummy train onto the dummy track to pick up
|
||||
passengers and making sure a full load is available when a pick-up
|
||||
train arrives. We do not place exit signals at the station. When a
|
||||
dummy train is inside the station it blocks the whole station due to
|
||||
the crossing tracks at the end of the station. The way-point is going
|
||||
to be used to open the station for the pick-up trains.
|
||||
|
||||
* Train Orders
|
||||
** Dummy Trains
|
||||
We create a dummy train per station. These collect passengers and
|
||||
block the station for the pick-up trains. Once a full load of
|
||||
passengers has been collected the dummy train unloads them and
|
||||
unblocks the station. Now a full load of passengers is available for
|
||||
the pick-up trains.
|
||||
|
||||
We make the trains length four so that it can drive around in the
|
||||
station itself, which has length five. We are using "near end" and "far
|
||||
end" orders:
|
||||
|
||||
1. Go to near end of the station and full load
|
||||
2. Go to far end of the station and transfer
|
||||
3. Go to the way-point after the station.
|
||||
|
||||
Step three is what unblocks the station. Once the train has left the
|
||||
depot we have to remove the track right in front of it so that the
|
||||
dummy train cannot re-enter the depot. Alternatively we could put the
|
||||
depot right after the way-point and delete it after the train left it.
|
||||
|
||||
[[file:openttd-srnw/dummy-train-orders.png][file:openttd-srnw/dummy-train-orders_small.png]]
|
||||
|
||||
** Pick-up Trains
|
||||
The pick-up trains have two orders:
|
||||
1. Go via INJECT way-point
|
||||
2. Go to transfer station and transfer.
|
||||
|
||||
Unfortunately OpenTTD will create implicit orders every time the train
|
||||
passes through one of the SBahn stations. To prevent this we add a
|
||||
"Jump to order 1" instruction and then fill up the orders with an
|
||||
unreachable way-point. A train can only have 255 orders. Once the
|
||||
order list is full no implicit orders can be created.
|
||||
|
||||
[[file:openttd-srnw/pick-up-train-orders.png][file:openttd-srnw/pick-up-train-orders_small.png]]
|
||||
|
||||
* Self Regulating Network Signalling
|
||||
The interesting bit happens once the trains passed the INJECT
|
||||
way-point. They are now inside of the waiting loop and try to get
|
||||
to the transfer station. This is only possible through one of the
|
||||
SBahn stations.
|
||||
|
||||
When a train reaches the tunnel entrance to an SBahn station it has
|
||||
two choices, either continue in the waiting loop or enter the station.
|
||||
|
||||
If a train is already in the waiting bay of that station we want our
|
||||
train in the waiting loop to continue and try the next station. We put
|
||||
two-way block signals at the tunnel entrance which turns red when the
|
||||
waiting bay is occupied by a train. OpenTTD's path finder treats a red
|
||||
two-way signal as a dead-end[fn::This is only true if
|
||||
=yapf.rail_firstred_twoway_eol= is =True=. This is the default in
|
||||
OpenTTD 13.]. It has an infinite penalty and any other
|
||||
path will be better. Our train continues. This is the first block
|
||||
signal in the picture below.
|
||||
|
||||
At our second SBahn entrance the waiting bay is free and the block
|
||||
signal shows green, so a train should enter the waiting bay towards
|
||||
the station. However, the path finder sees that the path goes through
|
||||
a station. Going through a station incurs a penalty for that path and
|
||||
it might so happen that the path continuing in the waiting loop is
|
||||
considered better, our train would not enter the waiting bay.
|
||||
|
||||
We need to add a penalty to the waiting loop path to make the path
|
||||
through the station look better. We do this by adding a few reversed
|
||||
path signals after the choice.
|
||||
|
||||
[[file:openttd-srnw/srnw-signalling.png][file:openttd-srnw/srnw-signalling_small.png]]
|
||||
|
||||
* Bonus: Path Based Signal Priority Merge
|
||||
The [[https://wiki.openttdcoop.org/Priorities#PBS_prio][Priority - #openttdcoop wiki]] article shows how to construct a
|
||||
priority merge using path based signals. Since I will likely forget it
|
||||
and the traditional priorities are slightly more difficult to build I
|
||||
put a note here.
|
||||
|
||||
The way this works is that the tracks coloured in red form one big
|
||||
signal block that reaches all the way to the merger at the front. Once
|
||||
the mainline train enters this block the entry signal for the merging
|
||||
train turns red and the merging train has to wait for the mainline
|
||||
train to clear the block.
|
||||
|
||||
[[file:openttd-srnw/pbs-prio.png][file:openttd-srnw/pbs-prio_small.png]]
|
||||
|
||||
* Epilogue
|
||||
After many iterations I came up with this city layout and station
|
||||
design. It is probably not the best one and different trade-offs can
|
||||
be made. The most fun in OpenTTD is to figure these things out and not
|
||||
just copying a design from somewhere. When I come back to playing
|
||||
OpenTTD in a few years I can start from this and try to improve on
|
||||
this and do not have to re-discover all this the hard way.
|
After Width: | Height: | Size: 5.2 MiB |
After Width: | Height: | Size: 673 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 610 KiB |
After Width: | Height: | Size: 6.1 MiB |
After Width: | Height: | Size: 554 KiB |
After Width: | Height: | Size: 4.7 MiB |
After Width: | Height: | Size: 709 KiB |
After Width: | Height: | Size: 4.9 MiB |
After Width: | Height: | Size: 681 KiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 6.6 MiB |
After Width: | Height: | Size: 634 KiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 929 KiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 849 KiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 778 KiB |
After Width: | Height: | Size: 2.5 MiB |
After Width: | Height: | Size: 575 KiB |
After Width: | Height: | Size: 1.9 MiB |
After Width: | Height: | Size: 688 KiB |
|
@ -52,7 +52,7 @@
|
|||
:time-stamp-file: nil
|
||||
:html-postamble "<p class=\"date\">Published: %d</p>
|
||||
<hr /><footer><nav><a href=\"/\">< Home</a></nav>
|
||||
Copyright © 2014 - 2023 Florian Obser. All rights reserved.
|
||||
Copyright © 2014 - 2024 Florian Obser. All rights reserved.
|
||||
</footer>"
|
||||
:html-head-include-default-style: nil
|
||||
:html-head "<link rel=\"stylesheet\" href=\"simple.min.css\" type=\"text/css\"/>
|
||||
|
|