From b868f3289b7b447310d665299af1f30a273d2eb1 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Mon, 9 Mar 2026 15:38:44 +0100 Subject: [PATCH 01/31] Add: Readme explaining the purpose, format and links to existing blog posts --- documents/docs/blog/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 documents/docs/blog/README.md diff --git a/documents/docs/blog/README.md b/documents/docs/blog/README.md new file mode 100644 index 0000000..aa92b15 --- /dev/null +++ b/documents/docs/blog/README.md @@ -0,0 +1,28 @@ +# Blog + +## Purpose +This folder contains a collection of blog posts documenting the development process of our application. These blogs serve two key purposes: + +1. **For External Audiences** + + The blogs provide insights into the internal program flow and the interaction between components without requiring readers to dive into the source code. They offer a mixed-level overview of how different parts of the system work together. + +2. **For Our Team** + + The blogs serve as a record of our decision-making process. By documenting what decisions were made and why, we can track the evolution of our design choices and understand the reasoning behind them. This is invaluable for maintaining consistency across the team. + +But keep in mind that these blogs **do not replace** the detailed source code documentation. + +## Format +All blog posts follow the naming convention: `_.md` + +## Blog Posts + +### General +*(No posts yet)* + +### Client +*(No posts yet)* + +### Server +*(No posts yet)* From ea8ac3831f6ff43c9547bc74f8c633ab8028f684 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 16:07:18 +0100 Subject: [PATCH 02/31] Add: Subfolders in repository structure for diary and blog in main readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 01ffb96..59bdd50 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ This protocol handles game state synchronization, player actions, lobby and glob │ └── test/ # Unit tests ├── documents/ # Project resources and documentation │ ├── images/ # Images for README and documentation -│ ├── docs # Documentation +│ ├── docs # Directory for diary and blog files +│ │ ├── diary # Diary files for each organizational meetup +| | └── blog # Blog files covering project-related topics │ ├── milestones/ # Milestone deliverables (6 milestones) │ └── (Other resources) # Additional project materials └── outreach/ # Public-facing content and marketing materials From d060697aa954ffe65ca6af7d0993e9af720fa06b Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 16:27:13 +0100 Subject: [PATCH 03/31] Add: Draw.io related files to .gitignore --- .gitignore | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a1b3b23..1a83ce7 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,17 @@ $RECYCLE.BIN/ .java-version ## VSCode -.vscode \ No newline at end of file +.vscode + +## diagrams.net / draw.io +# Draw.io temporary files +*.drawio.bkp +*.drawio.tmp + +# Auto-save files +*.drawio.autosave + +# Exported images (optional – nur wenn du Exporte nicht versionieren willst) +*.drawio.png +*.drawio.svg +*.drawio.pdf \ No newline at end of file From e7b95098b2dbffd54c4aee305ac744d746446b72 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 16:28:16 +0100 Subject: [PATCH 04/31] Add: Draw.io document and exported image for the blog --- ...shared_ressouce_possible_approaches.drawio | 100 ++++++++++++++++++ .../shared_ressouce_possible_approaches.svg | 3 + 2 files changed, 103 insertions(+) create mode 100644 documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio create mode 100644 documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg diff --git a/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio b/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio new file mode 100644 index 0000000..b6788a5 --- /dev/null +++ b/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio @@ -0,0 +1,100 @@ +<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.6.1 Chrome/142.0.7444.265 Electron/39.8.0 Safari/537.36" version="29.6.1"> + <diagram name="Seite-1" id="YJCt4VXcEaH1ZB-WJMOn"> + <mxGraphModel dx="901" dy="582" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> + <root> + <mxCell id="0" /> + <mxCell id="1" parent="0" /> + <mxCell id="C29U5SLjdgufHwaoauAd-1" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Peer To Peer" vertex="1"> + <mxGeometry height="30" width="140" x="70" y="51.61" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-2" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="70" y="160" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-3" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="160" y="160" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-4" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="160" y="90" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-5" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;fillColor=#ffe6cc;strokeColor=#d79b00;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="70" y="90" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-6" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="160" y="160" as="sourcePoint" /> + <mxPoint x="120" y="120" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-7" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="120" y="160" as="sourcePoint" /> + <mxPoint x="160" y="120" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-8" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="120" y="100" as="sourcePoint" /> + <mxPoint x="160" y="100" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-9" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="120" y="180" as="sourcePoint" /> + <mxPoint x="160" y="180" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-10" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="90" y="120" as="sourcePoint" /> + <mxPoint x="90" y="160" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-12" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="190" y="120" as="sourcePoint" /> + <mxPoint x="190" y="160" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-14" edge="1" parent="1" style="endArrow=none;dashed=1;html=1;rounded=0;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="240" y="200" as="sourcePoint" /> + <mxPoint x="240" y="60" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-15" parent="1" style="sketch=0;aspect=fixed;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;fillColor=#d5e8d4;shape=mxgraph.mscae.enterprise.server_farm" value="" vertex="1"> + <mxGeometry height="38.39" width="60" x="350" y="81.61" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-16" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="270" y="90" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-17" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="354.40999999999997" y="160" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-18" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> + <mxGeometry height="30" width="51.18" x="440" y="90" as="geometry" /> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-19" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="379.87" y="120" as="sourcePoint" /> + <mxPoint x="379.87" y="160" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-20" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="350" y="100" as="sourcePoint" /> + <mxPoint x="320" y="100" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-22" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> + <mxGeometry height="50" relative="1" width="50" as="geometry"> + <mxPoint x="440" y="100.73" as="sourcePoint" /> + <mxPoint x="410" y="100.73" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="C29U5SLjdgufHwaoauAd-23" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Client - Server" vertex="1"> + <mxGeometry height="30" width="220" x="270" y="40" as="geometry" /> + </mxCell> + </root> + </mxGraphModel> + </diagram> +</mxfile> diff --git a/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg b/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg new file mode 100644 index 0000000..5216099 --- /dev/null +++ b/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="424px" height="163px" viewBox="0 0 424 163"><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="C29U5SLjdgufHwaoauAd-1"><g transform="translate(0.5,0.5)"><rect x="0" y="11.61" width="140" height="30" fill="none" stroke="none" pointer-events="all"/></g><g><g><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 27px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Peer To Peer</div></div></div></foreignObject><image x="1" y="20.5" width="138" height="17" xlink:href="data:image/png;base64,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"/></switch></g></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-2"><g transform="translate(0.5,0.5)"><rect x="0" y="120" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 29.59 148.11 C 30.08 148.11 30.42 147.92 30.33 147.77 L 29.35 146.35 C 29.23 146.13 29.05 146.09 28.76 146.09 L 22.42 146.09 C 22.11 146.09 21.95 146.14 21.8 146.36 L 20.9 147.77 C 20.72 147.99 21.25 148.11 21.63 148.11 Z M 44.4 143.48 L 44.4 122.61 L 6.75 122.61 L 6.75 143.48 Z M 2.65 150 C 1.65 149.99 0.75 149.66 0.37 149.15 C 0 148.64 0.15 148.14 0.47 147.79 L 4.11 144.13 L 4.11 122.5 C 4.11 121.41 5.1 120 6.74 120 L 44.41 120 C 45.69 120 47.04 121.01 47.04 122.63 L 47.04 144.13 L 50.72 147.83 C 51.03 148.17 51.18 148.63 50.81 149.13 C 50.33 149.76 49.41 149.96 48.55 150 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-3"><g transform="translate(0.5,0.5)"><rect x="90" y="120" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 119.59 148.11 C 120.08 148.11 120.42 147.92 120.33 147.77 L 119.35 146.35 C 119.23 146.13 119.05 146.09 118.76 146.09 L 112.42 146.09 C 112.11 146.09 111.95 146.14 111.8 146.36 L 110.9 147.77 C 110.72 147.99 111.25 148.11 111.63 148.11 Z M 134.4 143.48 L 134.4 122.61 L 96.75 122.61 L 96.75 143.48 Z M 92.65 150 C 91.65 149.99 90.75 149.66 90.37 149.15 C 90 148.64 90.15 148.14 90.47 147.79 L 94.11 144.13 L 94.11 122.5 C 94.11 121.41 95.1 120 96.74 120 L 134.41 120 C 135.69 120 137.04 121.01 137.04 122.63 L 137.04 144.13 L 140.72 147.83 C 141.03 148.17 141.18 148.63 140.81 149.13 C 140.33 149.76 139.41 149.96 138.55 150 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-4"><g transform="translate(0.5,0.5)"><rect x="90" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 119.59 78.11 C 120.08 78.11 120.42 77.92 120.33 77.77 L 119.35 76.35 C 119.23 76.13 119.05 76.09 118.76 76.09 L 112.42 76.09 C 112.11 76.09 111.95 76.14 111.8 76.36 L 110.9 77.77 C 110.72 77.99 111.25 78.11 111.63 78.11 Z M 134.4 73.48 L 134.4 52.61 L 96.75 52.61 L 96.75 73.48 Z M 92.65 80 C 91.65 79.99 90.75 79.66 90.37 79.15 C 90 78.64 90.15 78.14 90.47 77.79 L 94.11 74.13 L 94.11 52.5 C 94.11 51.41 95.1 50 96.74 50 L 134.41 50 C 135.69 50 137.04 51.01 137.04 52.63 L 137.04 74.13 L 140.72 77.83 C 141.03 78.17 141.18 78.63 140.81 79.13 C 140.33 79.76 139.41 79.96 138.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-5"><g transform="translate(0.5,0.5)"><rect x="0" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 29.59 78.11 C 30.08 78.11 30.42 77.92 30.33 77.77 L 29.35 76.35 C 29.23 76.13 29.05 76.09 28.76 76.09 L 22.42 76.09 C 22.11 76.09 21.95 76.14 21.8 76.36 L 20.9 77.77 C 20.72 77.99 21.25 78.11 21.63 78.11 Z M 44.4 73.48 L 44.4 52.61 L 6.75 52.61 L 6.75 73.48 Z M 2.65 80 C 1.65 79.99 0.75 79.66 0.37 79.15 C 0 78.64 0.15 78.14 0.47 77.79 L 4.11 74.13 L 4.11 52.5 C 4.11 51.41 5.1 50 6.74 50 L 44.41 50 C 45.69 50 47.04 51.01 47.04 52.63 L 47.04 74.13 L 50.72 77.83 C 51.03 78.17 51.18 78.63 50.81 79.13 C 50.33 79.76 49.41 79.96 48.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-6"><g transform="translate(0.5,0.5)"><path d="M 85.5 115.5 L 54.5 84.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 89.21 119.21 L 81.78 116.73 L 85.5 115.5 L 86.73 111.78 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 50.79 80.79 L 58.22 83.27 L 54.5 84.5 L 53.27 88.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-7"><g transform="translate(0.5,0.5)"><path d="M 54.5 115.5 L 85.5 84.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 50.79 119.21 L 53.27 111.78 L 54.5 115.5 L 58.22 116.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 89.21 80.79 L 86.73 88.22 L 85.5 84.5 L 81.78 83.27 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-8"><g transform="translate(0.5,0.5)"><path d="M 56.37 60 L 83.63 60" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 51.12 60 L 58.12 56.5 L 56.37 60 L 58.12 63.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 88.88 60 L 81.88 63.5 L 83.63 60 L 81.88 56.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-9"><g transform="translate(0.5,0.5)"><path d="M 56.37 140 L 83.63 140" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 51.12 140 L 58.12 136.5 L 56.37 140 L 58.12 143.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 88.88 140 L 81.88 143.5 L 83.63 140 L 81.88 136.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-10"><g transform="translate(0.5,0.5)"><path d="M 20 86.37 L 20 113.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 20 81.12 L 23.5 88.12 L 20 86.37 L 16.5 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 20 118.88 L 16.5 111.88 L 20 113.63 L 23.5 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-12"><g transform="translate(0.5,0.5)"><path d="M 120 86.37 L 120 113.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 120 81.12 L 123.5 88.12 L 120 86.37 L 116.5 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 120 118.88 L 116.5 111.88 L 120 113.63 L 123.5 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-14"><g transform="translate(0.5,0.5)"><path d="M 170 160 L 170 20" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-15"><g transform="translate(0.5,0.5)"><rect x="280" y="41.61" width="60" height="38.39" fill="none" stroke="none" pointer-events="all"/><path d="M 280 80 L 280 43.19 C 280.11 42.33 280.82 41.67 281.69 41.62 L 296.75 41.62 C 297.24 41.62 297.71 41.82 298.05 42.17 C 298.39 42.52 298.57 43 298.55 43.49 L 298.55 80 Z M 283.25 76.73 L 295.3 76.73 L 295.3 74.43 L 283.25 74.43 Z M 295.3 72.25 L 295.3 70.07 L 283.25 70.07 L 283.25 72.37 L 295.3 72.37 Z M 295.3 49.3 L 295.3 47.06 L 283.25 47.06 L 283.25 49.3 Z M 300.72 80 L 300.72 43.19 C 300.84 42.28 301.62 41.61 302.53 41.62 L 317.53 41.62 C 318.42 41.64 319.16 42.31 319.28 43.19 L 319.28 80 Z M 303.98 76.73 L 316.02 76.73 L 316.02 74.43 L 303.98 74.43 Z M 316.02 72.37 L 316.02 70.07 L 303.98 70.07 L 303.98 72.37 Z M 303.98 49.3 L 316.02 49.3 L 316.02 47.06 L 303.98 47.06 Z M 321.51 80 L 321.51 43.19 C 321.6 42.41 322.18 41.77 322.95 41.62 L 338.31 41.62 C 339.18 41.67 339.89 42.33 340 43.19 L 340 80 Z M 324.7 76.73 L 336.81 76.73 L 336.81 74.43 L 324.7 74.43 Z M 336.81 72.37 L 336.81 70.07 L 324.7 70.07 L 324.7 72.37 Z M 324.7 49.3 L 336.81 49.3 L 336.81 47.06 L 324.7 47.06 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(213, 232, 212), rgb(31, 47, 30)); stroke: light-dark(rgb(130, 179, 102), rgb(68, 110, 44));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-16"><g transform="translate(0.5,0.5)"><rect x="200" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 229.59 78.11 C 230.08 78.11 230.42 77.92 230.33 77.77 L 229.35 76.35 C 229.23 76.13 229.05 76.09 228.76 76.09 L 222.42 76.09 C 222.11 76.09 221.95 76.14 221.8 76.36 L 220.9 77.77 C 220.72 77.99 221.25 78.11 221.63 78.11 Z M 244.4 73.48 L 244.4 52.61 L 206.75 52.61 L 206.75 73.48 Z M 202.65 80 C 201.65 79.99 200.75 79.66 200.37 79.15 C 200 78.64 200.15 78.14 200.47 77.79 L 204.11 74.13 L 204.11 52.5 C 204.11 51.41 205.1 50 206.74 50 L 244.41 50 C 245.69 50 247.04 51.01 247.04 52.63 L 247.04 74.13 L 250.72 77.83 C 251.03 78.17 251.18 78.63 250.81 79.13 C 250.33 79.76 249.41 79.96 248.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-17"><g transform="translate(0.5,0.5)"><rect x="284.41" y="120" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 314 148.11 C 314.49 148.11 314.83 147.92 314.74 147.77 L 313.76 146.35 C 313.64 146.13 313.46 146.09 313.17 146.09 L 306.83 146.09 C 306.52 146.09 306.36 146.14 306.21 146.36 L 305.31 147.77 C 305.13 147.99 305.66 148.11 306.04 148.11 Z M 328.81 143.48 L 328.81 122.61 L 291.16 122.61 L 291.16 143.48 Z M 287.06 150 C 286.06 149.99 285.16 149.66 284.78 149.15 C 284.41 148.64 284.56 148.14 284.88 147.79 L 288.52 144.13 L 288.52 122.5 C 288.52 121.41 289.51 120 291.15 120 L 328.82 120 C 330.1 120 331.45 121.01 331.45 122.63 L 331.45 144.13 L 335.13 147.83 C 335.44 148.17 335.59 148.63 335.22 149.13 C 334.74 149.76 333.82 149.96 332.96 150 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-18"><g transform="translate(0.5,0.5)"><rect x="370" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 399.59 78.11 C 400.08 78.11 400.42 77.92 400.33 77.77 L 399.35 76.35 C 399.23 76.13 399.05 76.09 398.76 76.09 L 392.42 76.09 C 392.11 76.09 391.95 76.14 391.8 76.36 L 390.9 77.77 C 390.72 77.99 391.25 78.11 391.63 78.11 Z M 414.4 73.48 L 414.4 52.61 L 376.75 52.61 L 376.75 73.48 Z M 372.65 80 C 371.65 79.99 370.75 79.66 370.37 79.15 C 370 78.64 370.15 78.14 370.47 77.79 L 374.11 74.13 L 374.11 52.5 C 374.11 51.41 375.1 50 376.74 50 L 414.41 50 C 415.69 50 417.04 51.01 417.04 52.63 L 417.04 74.13 L 420.72 77.83 C 421.03 78.17 421.18 78.63 420.81 79.13 C 420.33 79.76 419.41 79.96 418.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-19"><g transform="translate(0.5,0.5)"><path d="M 309.87 86.37 L 309.87 113.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 309.87 81.12 L 313.37 88.12 L 309.87 86.37 L 306.37 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 309.87 118.88 L 306.37 111.88 L 309.87 113.63 L 313.37 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-20"><g transform="translate(0.5,0.5)"><path d="M 273.63 60 L 256.37 60" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 278.88 60 L 271.88 63.5 L 273.63 60 L 271.88 56.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 251.12 60 L 258.12 56.5 L 256.37 60 L 258.12 63.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-22"><g transform="translate(0.5,0.5)"><path d="M 363.63 60.73 L 346.37 60.73" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 368.88 60.73 L 361.88 64.23 L 363.63 60.73 L 361.88 57.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 341.12 60.73 L 348.12 57.23 L 346.37 60.73 L 348.12 64.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-23"><g transform="translate(0.5,0.5)"><rect x="200" y="0" width="220" height="30" fill="none" stroke="none" pointer-events="all"/></g><g><g><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 15px; margin-left: 201px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Client - Server</div></div></div></foreignObject><image x="201" y="8.5" width="218" height="17" xlink:href="data:image/png;base64,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"/></switch></g></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file From 8e52ed21a5070e7b468ed16002a4750a858e4a89 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 17:34:51 +0100 Subject: [PATCH 05/31] Add: Remove .vscode folder from gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index a1b3b23..49611dd 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,3 @@ $RECYCLE.BIN/ ## Jenv .java-version - -## VSCode -.vscode \ No newline at end of file From 64742d6aad461e03457df8cae50224fc9aa3b779 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 17:57:05 +0100 Subject: [PATCH 06/31] Add: Recommended extensions for this workspace --- .vscode/extensions.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..6fb7f20 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "vscjava.vscode-java-pack", + "mhutchie.git-graph", + "gitlab.gitlab-workflow", + ] +} \ No newline at end of file From bf6873cfd7239562fbade86027e55a3a588c2028 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 18:05:21 +0100 Subject: [PATCH 07/31] Add: Bash script for in-place converting all found .drawio files to svg's --- scripts/export-drawio.sh | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100755 scripts/export-drawio.sh diff --git a/scripts/export-drawio.sh b/scripts/export-drawio.sh new file mode 100755 index 0000000..d00d8b8 --- /dev/null +++ b/scripts/export-drawio.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Check for draw.io installation +DRAWIO_PATH="" +check_drawio_installed() { + if command -v drawio &>/dev/null; then + DRAWIO_PATH=$(command -v drawio) + return 0 # drawio via PATH + elif [[ -x "/Applications/draw.io.app/Contents/MacOS/draw.io" ]]; then + DRAWIO_PATH="/Applications/draw.io.app/Contents/MacOS/draw.io" + return 0 # For macOS check at default path + else + return 1 # not found + fi +} + +if ! check_drawio_installed; then + echo "Error: 'drawio' was not found. Make sure, that the draw.io cli is installed." + echo "https://www.diagrams.net/" + exit 1 +else + echo "Draw.io installation found at: $DRAWIO_PATH" +fi + +needs_export() { + local drawio_file="$1" + local svg_file="$2" + + # If no file found, export + if [[ ! -f "$svg_file" ]]; then + return 0 + fi + + # Compare source (draw.io document) and exported file on mtime + if [[ "$drawio_file" -nt "$svg_file" ]]; then + return 0 + else + return 1 + fi +} + +export_file() { + local drawio_file="$1" + local svg_file="${drawio_file%.drawio}.svg" + + if needs_export "$drawio_file" "$svg_file"; then + echo "↻ Exportiere: $drawio_file" + echo "$svg_file" + echo "$drawio_file" + + "$DRAWIO_PATH" --export --format svg --transparent --output "$svg_file" "$drawio_file" + else + echo "✓ Up-to-date: $drawio_file" + fi +} + +main() { + # Source - https://stackoverflow.com/a/9612232 + # Posted by Kevin, modified by community. See post 'Timeline' for change history + # Retrieved 2026-03-09, License - CC BY-SA 4.0 + find . -name '*.drawio' -print0 | + while IFS= read -r -d '' line; do + export_file "$line" + done + + echo "✓ Exported all $file_count files" +} + +# Start script at entry point +main From 8579f15868761ca2160f2a81badcc58c5ef5763d Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 18:06:19 +0100 Subject: [PATCH 08/31] Add: 'Export drawio to SVG'-task, executable via vscode --- .vscode/tasks.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..101c28b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Export drawio to SVG", + "type": "shell", + "command": "./scripts/export-drawio.sh", + "problemMatcher": [] + } + ] +} \ No newline at end of file From 76c182313e116ba1756cc494eafecb34e8d8d54f Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 18:07:49 +0100 Subject: [PATCH 09/31] Fix: Remove leftover variable in echo --- scripts/export-drawio.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/export-drawio.sh b/scripts/export-drawio.sh index d00d8b8..764dfe2 100755 --- a/scripts/export-drawio.sh +++ b/scripts/export-drawio.sh @@ -63,7 +63,7 @@ main() { export_file "$line" done - echo "✓ Exported all $file_count files" + echo "✓ Exported all files" } # Start script at entry point From dc784050bf6451f875e9b1ed9f034f42b139d5a6 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 18:11:18 +0100 Subject: [PATCH 10/31] Style: Add comments and overall cleanup --- scripts/export-drawio.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/export-drawio.sh b/scripts/export-drawio.sh index 764dfe2..f075d01 100755 --- a/scripts/export-drawio.sh +++ b/scripts/export-drawio.sh @@ -5,12 +5,12 @@ DRAWIO_PATH="" check_drawio_installed() { if command -v drawio &>/dev/null; then DRAWIO_PATH=$(command -v drawio) - return 0 # drawio via PATH + return 0 # drawio via PATH elif [[ -x "/Applications/draw.io.app/Contents/MacOS/draw.io" ]]; then DRAWIO_PATH="/Applications/draw.io.app/Contents/MacOS/draw.io" - return 0 # For macOS check at default path + return 0 # For macOS check at default path else - return 1 # not found + return 1 # not found fi } @@ -22,6 +22,7 @@ else echo "Draw.io installation found at: $DRAWIO_PATH" fi +# Compare source (draw.io document) and exported file svg on mtime needs_export() { local drawio_file="$1" local svg_file="$2" @@ -31,7 +32,6 @@ needs_export() { return 0 fi - # Compare source (draw.io document) and exported file on mtime if [[ "$drawio_file" -nt "$svg_file" ]]; then return 0 else @@ -39,12 +39,13 @@ needs_export() { fi } +# If (re-)export is necessary export with drawio cli export_file() { local drawio_file="$1" local svg_file="${drawio_file%.drawio}.svg" if needs_export "$drawio_file" "$svg_file"; then - echo "↻ Exportiere: $drawio_file" + echo "↻ Exporting: $drawio_file" echo "$svg_file" echo "$drawio_file" @@ -54,6 +55,7 @@ export_file() { fi } +# Iterate over all .drawio files main() { # Source - https://stackoverflow.com/a/9612232 # Posted by Kevin, modified by community. See post 'Timeline' for change history From d346ec4f9706a854d7c4f9f7eca0e695cadcd5a1 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 18:30:08 +0100 Subject: [PATCH 11/31] Add: Blog to cover networking in its entirety --- documents/docs/blog/09-03-26_Network.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 documents/docs/blog/09-03-26_Network.md diff --git a/documents/docs/blog/09-03-26_Network.md b/documents/docs/blog/09-03-26_Network.md new file mode 100644 index 0000000..c83c7a1 --- /dev/null +++ b/documents/docs/blog/09-03-26_Network.md @@ -0,0 +1,21 @@ +# Table of Contents + + +# Basics +## Formal concepts of networking +Whenever you want to access the same data on different devices without carrying it (the data) from device to device, with, let’s say, a thumb drive, you have multiple options for achieving this. + +![Peer To Peer Versus Client-Server Architecture](/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg) + +<!-- I tried to get this embeddet image to show up, but it just wouldnt +<img src="/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg" alt="Peer To Peer Versus Client-Server Architecture" width="200"/> +--> + +**Peer to Peer** enables users to share content (files, messages, ...). +While it does not rely on a central server, it comes with the big trade-of that the device storing the data you want to access has to be reachable and online. +Today Peer to Peer is still widely used with VoIP and decentralized networks for censorship-resistant transactions formaly known as the blockchain. + +The **Client-Server** model evolved into the sophisticated "web-services" we know and use daily. +One central powerful computer (the server) stores data and allows external devices (clients) to access it. + +For our course "Programmierprojekt" we are required to use the Client-Server model. From 676e24e78da6a87884659415cb4da88137792fe4 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Mon, 9 Mar 2026 18:59:35 +0100 Subject: [PATCH 12/31] Add: Table of contents and 'The Language of the Network' section --- documents/docs/blog/09-03-26_Network.md | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/documents/docs/blog/09-03-26_Network.md b/documents/docs/blog/09-03-26_Network.md index c83c7a1..e1ee3a4 100644 --- a/documents/docs/blog/09-03-26_Network.md +++ b/documents/docs/blog/09-03-26_Network.md @@ -1,4 +1,8 @@ # Table of Contents +- [Table of Contents](#table-of-contents) +- [Introduction](#introduction) + - [Formal concepts of networking](#formal-concepts-of-networking) + - [The Language of the Network](#the-language-of-the-network) # Basics @@ -19,3 +23,38 @@ The **Client-Server** model evolved into the sophisticated "web-services" we kno One central powerful computer (the server) stores data and allows external devices (clients) to access it. For our course "Programmierprojekt" we are required to use the Client-Server model. + +## The Language of the Network +To communicate over the network, we first need to establish a few rules. + +If we look at the real world, language also follows certain rules: + +* Grammar +* Meaning of Words + +In computer science, these rules are known as a "protocol." Everyone must follow these rules, because if not, the program we want to communicate with wouldn’t understand us, or in the worst case, misunderstand us. + +For our network, we’ve decided to build upon the Post Office Protocol (POP) version 3. It follows the request-response model, is synchronous, and meets our requirements. + +An example of a request and response (for our game) might look like this: + +1. Request + ```text + GET_DELTA 42 + ``` + +2. Response + ```text + +OK + PLAYER_FOLDED playerId=3 + POT 240 + NEXT_TURN playerId=1 + WHISPER from=player2 msg="hey" + CHAT_LOBBY player3="hello" + . + ``` + +The client requests the occurred actions since the 42nd state. + +The server responds with all the actions other players have made and the messages the client has received. + From 2dd3ea256260da9836eef030cd8d8ee35699ff9f Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 12:40:09 +0100 Subject: [PATCH 13/31] Docs: Delete documents related to '09-03-26_Network' post --- ...shared_ressouce_possible_approaches.drawio | 100 ------------------ .../shared_ressouce_possible_approaches.svg | 3 - 2 files changed, 103 deletions(-) delete mode 100644 documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio delete mode 100644 documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg diff --git a/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio b/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio deleted file mode 100644 index b6788a5..0000000 --- a/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.drawio +++ /dev/null @@ -1,100 +0,0 @@ -<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.6.1 Chrome/142.0.7444.265 Electron/39.8.0 Safari/537.36" version="29.6.1"> - <diagram name="Seite-1" id="YJCt4VXcEaH1ZB-WJMOn"> - <mxGraphModel dx="901" dy="582" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> - <root> - <mxCell id="0" /> - <mxCell id="1" parent="0" /> - <mxCell id="C29U5SLjdgufHwaoauAd-1" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Peer To Peer" vertex="1"> - <mxGeometry height="30" width="140" x="70" y="51.61" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-2" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="70" y="160" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-3" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="160" y="160" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-4" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="160" y="90" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-5" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;fillColor=#ffe6cc;strokeColor=#d79b00;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="70" y="90" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-6" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="160" y="160" as="sourcePoint" /> - <mxPoint x="120" y="120" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-7" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="120" y="160" as="sourcePoint" /> - <mxPoint x="160" y="120" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-8" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="120" y="100" as="sourcePoint" /> - <mxPoint x="160" y="100" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-9" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="120" y="180" as="sourcePoint" /> - <mxPoint x="160" y="180" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-10" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="90" y="120" as="sourcePoint" /> - <mxPoint x="90" y="160" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-12" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="190" y="120" as="sourcePoint" /> - <mxPoint x="190" y="160" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-14" edge="1" parent="1" style="endArrow=none;dashed=1;html=1;rounded=0;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="240" y="200" as="sourcePoint" /> - <mxPoint x="240" y="60" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-15" parent="1" style="sketch=0;aspect=fixed;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;fillColor=#d5e8d4;shape=mxgraph.mscae.enterprise.server_farm" value="" vertex="1"> - <mxGeometry height="38.39" width="60" x="350" y="81.61" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-16" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="270" y="90" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-17" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="354.40999999999997" y="160" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-18" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.devices.laptop;" value="" vertex="1"> - <mxGeometry height="30" width="51.18" x="440" y="90" as="geometry" /> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-19" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="379.87" y="120" as="sourcePoint" /> - <mxPoint x="379.87" y="160" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-20" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="350" y="100" as="sourcePoint" /> - <mxPoint x="320" y="100" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-22" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;startSize=6;" value=""> - <mxGeometry height="50" relative="1" width="50" as="geometry"> - <mxPoint x="440" y="100.73" as="sourcePoint" /> - <mxPoint x="410" y="100.73" as="targetPoint" /> - </mxGeometry> - </mxCell> - <mxCell id="C29U5SLjdgufHwaoauAd-23" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Client - Server" vertex="1"> - <mxGeometry height="30" width="220" x="270" y="40" as="geometry" /> - </mxCell> - </root> - </mxGraphModel> - </diagram> -</mxfile> diff --git a/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg b/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg deleted file mode 100644 index 5216099..0000000 --- a/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="424px" height="163px" viewBox="0 0 424 163"><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="C29U5SLjdgufHwaoauAd-1"><g transform="translate(0.5,0.5)"><rect x="0" y="11.61" width="140" height="30" fill="none" stroke="none" pointer-events="all"/></g><g><g><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 27px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Peer To Peer</div></div></div></foreignObject><image x="1" y="20.5" width="138" height="17" xlink:href="data:image/png;base64,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"/></switch></g></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-2"><g transform="translate(0.5,0.5)"><rect x="0" y="120" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 29.59 148.11 C 30.08 148.11 30.42 147.92 30.33 147.77 L 29.35 146.35 C 29.23 146.13 29.05 146.09 28.76 146.09 L 22.42 146.09 C 22.11 146.09 21.95 146.14 21.8 146.36 L 20.9 147.77 C 20.72 147.99 21.25 148.11 21.63 148.11 Z M 44.4 143.48 L 44.4 122.61 L 6.75 122.61 L 6.75 143.48 Z M 2.65 150 C 1.65 149.99 0.75 149.66 0.37 149.15 C 0 148.64 0.15 148.14 0.47 147.79 L 4.11 144.13 L 4.11 122.5 C 4.11 121.41 5.1 120 6.74 120 L 44.41 120 C 45.69 120 47.04 121.01 47.04 122.63 L 47.04 144.13 L 50.72 147.83 C 51.03 148.17 51.18 148.63 50.81 149.13 C 50.33 149.76 49.41 149.96 48.55 150 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-3"><g transform="translate(0.5,0.5)"><rect x="90" y="120" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 119.59 148.11 C 120.08 148.11 120.42 147.92 120.33 147.77 L 119.35 146.35 C 119.23 146.13 119.05 146.09 118.76 146.09 L 112.42 146.09 C 112.11 146.09 111.95 146.14 111.8 146.36 L 110.9 147.77 C 110.72 147.99 111.25 148.11 111.63 148.11 Z M 134.4 143.48 L 134.4 122.61 L 96.75 122.61 L 96.75 143.48 Z M 92.65 150 C 91.65 149.99 90.75 149.66 90.37 149.15 C 90 148.64 90.15 148.14 90.47 147.79 L 94.11 144.13 L 94.11 122.5 C 94.11 121.41 95.1 120 96.74 120 L 134.41 120 C 135.69 120 137.04 121.01 137.04 122.63 L 137.04 144.13 L 140.72 147.83 C 141.03 148.17 141.18 148.63 140.81 149.13 C 140.33 149.76 139.41 149.96 138.55 150 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-4"><g transform="translate(0.5,0.5)"><rect x="90" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 119.59 78.11 C 120.08 78.11 120.42 77.92 120.33 77.77 L 119.35 76.35 C 119.23 76.13 119.05 76.09 118.76 76.09 L 112.42 76.09 C 112.11 76.09 111.95 76.14 111.8 76.36 L 110.9 77.77 C 110.72 77.99 111.25 78.11 111.63 78.11 Z M 134.4 73.48 L 134.4 52.61 L 96.75 52.61 L 96.75 73.48 Z M 92.65 80 C 91.65 79.99 90.75 79.66 90.37 79.15 C 90 78.64 90.15 78.14 90.47 77.79 L 94.11 74.13 L 94.11 52.5 C 94.11 51.41 95.1 50 96.74 50 L 134.41 50 C 135.69 50 137.04 51.01 137.04 52.63 L 137.04 74.13 L 140.72 77.83 C 141.03 78.17 141.18 78.63 140.81 79.13 C 140.33 79.76 139.41 79.96 138.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-5"><g transform="translate(0.5,0.5)"><rect x="0" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 29.59 78.11 C 30.08 78.11 30.42 77.92 30.33 77.77 L 29.35 76.35 C 29.23 76.13 29.05 76.09 28.76 76.09 L 22.42 76.09 C 22.11 76.09 21.95 76.14 21.8 76.36 L 20.9 77.77 C 20.72 77.99 21.25 78.11 21.63 78.11 Z M 44.4 73.48 L 44.4 52.61 L 6.75 52.61 L 6.75 73.48 Z M 2.65 80 C 1.65 79.99 0.75 79.66 0.37 79.15 C 0 78.64 0.15 78.14 0.47 77.79 L 4.11 74.13 L 4.11 52.5 C 4.11 51.41 5.1 50 6.74 50 L 44.41 50 C 45.69 50 47.04 51.01 47.04 52.63 L 47.04 74.13 L 50.72 77.83 C 51.03 78.17 51.18 78.63 50.81 79.13 C 50.33 79.76 49.41 79.96 48.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-6"><g transform="translate(0.5,0.5)"><path d="M 85.5 115.5 L 54.5 84.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 89.21 119.21 L 81.78 116.73 L 85.5 115.5 L 86.73 111.78 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 50.79 80.79 L 58.22 83.27 L 54.5 84.5 L 53.27 88.22 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-7"><g transform="translate(0.5,0.5)"><path d="M 54.5 115.5 L 85.5 84.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 50.79 119.21 L 53.27 111.78 L 54.5 115.5 L 58.22 116.73 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 89.21 80.79 L 86.73 88.22 L 85.5 84.5 L 81.78 83.27 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-8"><g transform="translate(0.5,0.5)"><path d="M 56.37 60 L 83.63 60" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 51.12 60 L 58.12 56.5 L 56.37 60 L 58.12 63.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 88.88 60 L 81.88 63.5 L 83.63 60 L 81.88 56.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-9"><g transform="translate(0.5,0.5)"><path d="M 56.37 140 L 83.63 140" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 51.12 140 L 58.12 136.5 L 56.37 140 L 58.12 143.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 88.88 140 L 81.88 143.5 L 83.63 140 L 81.88 136.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-10"><g transform="translate(0.5,0.5)"><path d="M 20 86.37 L 20 113.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 20 81.12 L 23.5 88.12 L 20 86.37 L 16.5 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 20 118.88 L 16.5 111.88 L 20 113.63 L 23.5 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-12"><g transform="translate(0.5,0.5)"><path d="M 120 86.37 L 120 113.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 120 81.12 L 123.5 88.12 L 120 86.37 L 116.5 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 120 118.88 L 116.5 111.88 L 120 113.63 L 123.5 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-14"><g transform="translate(0.5,0.5)"><path d="M 170 160 L 170 20" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-15"><g transform="translate(0.5,0.5)"><rect x="280" y="41.61" width="60" height="38.39" fill="none" stroke="none" pointer-events="all"/><path d="M 280 80 L 280 43.19 C 280.11 42.33 280.82 41.67 281.69 41.62 L 296.75 41.62 C 297.24 41.62 297.71 41.82 298.05 42.17 C 298.39 42.52 298.57 43 298.55 43.49 L 298.55 80 Z M 283.25 76.73 L 295.3 76.73 L 295.3 74.43 L 283.25 74.43 Z M 295.3 72.25 L 295.3 70.07 L 283.25 70.07 L 283.25 72.37 L 295.3 72.37 Z M 295.3 49.3 L 295.3 47.06 L 283.25 47.06 L 283.25 49.3 Z M 300.72 80 L 300.72 43.19 C 300.84 42.28 301.62 41.61 302.53 41.62 L 317.53 41.62 C 318.42 41.64 319.16 42.31 319.28 43.19 L 319.28 80 Z M 303.98 76.73 L 316.02 76.73 L 316.02 74.43 L 303.98 74.43 Z M 316.02 72.37 L 316.02 70.07 L 303.98 70.07 L 303.98 72.37 Z M 303.98 49.3 L 316.02 49.3 L 316.02 47.06 L 303.98 47.06 Z M 321.51 80 L 321.51 43.19 C 321.6 42.41 322.18 41.77 322.95 41.62 L 338.31 41.62 C 339.18 41.67 339.89 42.33 340 43.19 L 340 80 Z M 324.7 76.73 L 336.81 76.73 L 336.81 74.43 L 324.7 74.43 Z M 336.81 72.37 L 336.81 70.07 L 324.7 70.07 L 324.7 72.37 Z M 324.7 49.3 L 336.81 49.3 L 336.81 47.06 L 324.7 47.06 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(213, 232, 212), rgb(31, 47, 30)); stroke: light-dark(rgb(130, 179, 102), rgb(68, 110, 44));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-16"><g transform="translate(0.5,0.5)"><rect x="200" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 229.59 78.11 C 230.08 78.11 230.42 77.92 230.33 77.77 L 229.35 76.35 C 229.23 76.13 229.05 76.09 228.76 76.09 L 222.42 76.09 C 222.11 76.09 221.95 76.14 221.8 76.36 L 220.9 77.77 C 220.72 77.99 221.25 78.11 221.63 78.11 Z M 244.4 73.48 L 244.4 52.61 L 206.75 52.61 L 206.75 73.48 Z M 202.65 80 C 201.65 79.99 200.75 79.66 200.37 79.15 C 200 78.64 200.15 78.14 200.47 77.79 L 204.11 74.13 L 204.11 52.5 C 204.11 51.41 205.1 50 206.74 50 L 244.41 50 C 245.69 50 247.04 51.01 247.04 52.63 L 247.04 74.13 L 250.72 77.83 C 251.03 78.17 251.18 78.63 250.81 79.13 C 250.33 79.76 249.41 79.96 248.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-17"><g transform="translate(0.5,0.5)"><rect x="284.41" y="120" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 314 148.11 C 314.49 148.11 314.83 147.92 314.74 147.77 L 313.76 146.35 C 313.64 146.13 313.46 146.09 313.17 146.09 L 306.83 146.09 C 306.52 146.09 306.36 146.14 306.21 146.36 L 305.31 147.77 C 305.13 147.99 305.66 148.11 306.04 148.11 Z M 328.81 143.48 L 328.81 122.61 L 291.16 122.61 L 291.16 143.48 Z M 287.06 150 C 286.06 149.99 285.16 149.66 284.78 149.15 C 284.41 148.64 284.56 148.14 284.88 147.79 L 288.52 144.13 L 288.52 122.5 C 288.52 121.41 289.51 120 291.15 120 L 328.82 120 C 330.1 120 331.45 121.01 331.45 122.63 L 331.45 144.13 L 335.13 147.83 C 335.44 148.17 335.59 148.63 335.22 149.13 C 334.74 149.76 333.82 149.96 332.96 150 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-18"><g transform="translate(0.5,0.5)"><rect x="370" y="50" width="51.18" height="30" fill="none" stroke="none" pointer-events="all"/><path d="M 399.59 78.11 C 400.08 78.11 400.42 77.92 400.33 77.77 L 399.35 76.35 C 399.23 76.13 399.05 76.09 398.76 76.09 L 392.42 76.09 C 392.11 76.09 391.95 76.14 391.8 76.36 L 390.9 77.77 C 390.72 77.99 391.25 78.11 391.63 78.11 Z M 414.4 73.48 L 414.4 52.61 L 376.75 52.61 L 376.75 73.48 Z M 372.65 80 C 371.65 79.99 370.75 79.66 370.37 79.15 C 370 78.64 370.15 78.14 370.47 77.79 L 374.11 74.13 L 374.11 52.5 C 374.11 51.41 375.1 50 376.74 50 L 414.41 50 C 415.69 50 417.04 51.01 417.04 52.63 L 417.04 74.13 L 420.72 77.83 C 421.03 78.17 421.18 78.63 420.81 79.13 C 420.33 79.76 419.41 79.96 418.55 80 Z" fill="#ffe6cc" stroke="#d79b00" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(255, 230, 204), rgb(54, 33, 10)); stroke: light-dark(rgb(215, 155, 0), rgb(153, 101, 0));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-19"><g transform="translate(0.5,0.5)"><path d="M 309.87 86.37 L 309.87 113.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 309.87 81.12 L 313.37 88.12 L 309.87 86.37 L 306.37 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 309.87 118.88 L 306.37 111.88 L 309.87 113.63 L 313.37 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-20"><g transform="translate(0.5,0.5)"><path d="M 273.63 60 L 256.37 60" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 278.88 60 L 271.88 63.5 L 273.63 60 L 271.88 56.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 251.12 60 L 258.12 56.5 L 256.37 60 L 258.12 63.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-22"><g transform="translate(0.5,0.5)"><path d="M 363.63 60.73 L 346.37 60.73" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke" style="stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 368.88 60.73 L 361.88 64.23 L 363.63 60.73 L 361.88 57.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/><path d="M 341.12 60.73 L 348.12 57.23 L 346.37 60.73 L 348.12 64.23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all" style="fill: light-dark(rgb(0, 0, 0), rgb(255, 255, 255)); stroke: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));"/></g></g><g data-cell-id="C29U5SLjdgufHwaoauAd-23"><g transform="translate(0.5,0.5)"><rect x="200" y="0" width="220" height="30" fill="none" stroke="none" pointer-events="all"/></g><g><g><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 15px; margin-left: 201px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; color: #000000; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: light-dark(#000000, #ffffff); line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Client - Server</div></div></div></foreignObject><image x="201" y="8.5" width="218" height="17" xlink:href="data:image/png;base64,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"/></switch></g></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file From 86303f8b845a699d16c68f1070c62ba0fa7d725c Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 12:53:38 +0100 Subject: [PATCH 14/31] Docs: Delete '09-03-26_Networking' post --- documents/docs/blog/09-03-26_Network.md | 60 ------------------------- 1 file changed, 60 deletions(-) delete mode 100644 documents/docs/blog/09-03-26_Network.md diff --git a/documents/docs/blog/09-03-26_Network.md b/documents/docs/blog/09-03-26_Network.md deleted file mode 100644 index e1ee3a4..0000000 --- a/documents/docs/blog/09-03-26_Network.md +++ /dev/null @@ -1,60 +0,0 @@ -# Table of Contents -- [Table of Contents](#table-of-contents) -- [Introduction](#introduction) - - [Formal concepts of networking](#formal-concepts-of-networking) - - [The Language of the Network](#the-language-of-the-network) - - -# Basics -## Formal concepts of networking -Whenever you want to access the same data on different devices without carrying it (the data) from device to device, with, let’s say, a thumb drive, you have multiple options for achieving this. - -![Peer To Peer Versus Client-Server Architecture](/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg) - -<!-- I tried to get this embeddet image to show up, but it just wouldnt -<img src="/documents/images/docs/blog/09-03-26_Network/shared_ressouce_possible_approaches.svg" alt="Peer To Peer Versus Client-Server Architecture" width="200"/> ---> - -**Peer to Peer** enables users to share content (files, messages, ...). -While it does not rely on a central server, it comes with the big trade-of that the device storing the data you want to access has to be reachable and online. -Today Peer to Peer is still widely used with VoIP and decentralized networks for censorship-resistant transactions formaly known as the blockchain. - -The **Client-Server** model evolved into the sophisticated "web-services" we know and use daily. -One central powerful computer (the server) stores data and allows external devices (clients) to access it. - -For our course "Programmierprojekt" we are required to use the Client-Server model. - -## The Language of the Network -To communicate over the network, we first need to establish a few rules. - -If we look at the real world, language also follows certain rules: - -* Grammar -* Meaning of Words - -In computer science, these rules are known as a "protocol." Everyone must follow these rules, because if not, the program we want to communicate with wouldn’t understand us, or in the worst case, misunderstand us. - -For our network, we’ve decided to build upon the Post Office Protocol (POP) version 3. It follows the request-response model, is synchronous, and meets our requirements. - -An example of a request and response (for our game) might look like this: - -1. Request - ```text - GET_DELTA 42 - ``` - -2. Response - ```text - +OK - PLAYER_FOLDED playerId=3 - POT 240 - NEXT_TURN playerId=1 - WHISPER from=player2 msg="hey" - CHAT_LOBBY player3="hello" - . - ``` - -The client requests the occurred actions since the 42nd state. - -The server responds with all the actions other players have made and the messages the client has received. - From d0ea56daa4748a66cfa3786204d70a8c354473ef Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 13:18:50 +0100 Subject: [PATCH 15/31] Chore: Move 'blog' folder to top of 'documents' folder --- documents/{docs => }/blog/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename documents/{docs => }/blog/README.md (100%) diff --git a/documents/docs/blog/README.md b/documents/blog/README.md similarity index 100% rename from documents/docs/blog/README.md rename to documents/blog/README.md From b9878a9c336307e7ac33cef4499ad9664ab81bc5 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 13:19:17 +0100 Subject: [PATCH 16/31] Chore: Move 'diary' folder to top of 'documents' folder --- documents/{docs => }/diary/24-02.md | 0 documents/{docs => }/diary/27-02.md | 0 documents/{docs => }/diary/README.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename documents/{docs => }/diary/24-02.md (100%) rename documents/{docs => }/diary/27-02.md (100%) rename documents/{docs => }/diary/README.md (100%) diff --git a/documents/docs/diary/24-02.md b/documents/diary/24-02.md similarity index 100% rename from documents/docs/diary/24-02.md rename to documents/diary/24-02.md diff --git a/documents/docs/diary/27-02.md b/documents/diary/27-02.md similarity index 100% rename from documents/docs/diary/27-02.md rename to documents/diary/27-02.md diff --git a/documents/docs/diary/README.md b/documents/diary/README.md similarity index 100% rename from documents/docs/diary/README.md rename to documents/diary/README.md From 827ba59c7b998364a955f5598fdada78d01f42ce Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 13:20:50 +0100 Subject: [PATCH 17/31] Docs: Update repository structure in main README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59bdd50..bd9fc2c 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,9 @@ This protocol handles game state synchronization, player actions, lobby and glob │ └── test/ # Unit tests ├── documents/ # Project resources and documentation │ ├── images/ # Images for README and documentation -│ ├── docs # Directory for diary and blog files -│ │ ├── diary # Diary files for each organizational meetup -| | └── blog # Blog files covering project-related topics +│ ├── diary # Diary files for each organizational meetup +│ ├── blog # Blog files covering project-related topics +│ ├── docs # Documentation │ ├── milestones/ # Milestone deliverables (6 milestones) │ └── (Other resources) # Additional project materials └── outreach/ # Public-facing content and marketing materials From 2183241e08ffaf173da5ba5b1640160475183a55 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 17:28:31 +0100 Subject: [PATCH 18/31] Add: Script to convert puml file to svg via plantuml --- scripts/export-plantuml.sh | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 scripts/export-plantuml.sh diff --git a/scripts/export-plantuml.sh b/scripts/export-plantuml.sh new file mode 100755 index 0000000..59404e9 --- /dev/null +++ b/scripts/export-plantuml.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# Check for plantuml installation +PLANTUML_PATH="" +check_plantuml_installed() { + if command -v plantuml &>/dev/null; then + PLANTUML_PATH=$(command -v plantuml) + return 0 # plantuml via PATH + else + return 1 # not found + fi +} + +if ! check_plantuml_installed; then + echo "Error: 'plantuml' was not found. Make sure, that plantuml is installed." + echo "https://plantuml.com/starting" + exit 1 +else + echo "PlantUML installation found at: $PLANTUML_PATH" +fi + +# Compare source (.puml document) and exported file svg on mtime +needs_export() { + local puml_file="$1" + local svg_file="$2" + + # If no file found, export + if [[ ! -f "$svg_file" ]]; then + return 0 + fi + + if [[ "$puml_file" -nt "$svg_file" ]]; then + return 0 + else + return 1 + fi +} + +# If (re-)export is necessary export with plantuml cli +export_file() { + local puml_file="$1" + local svg_file="${puml_file%.puml}.svg" + + if needs_export "$puml_file" "$svg_file"; then + echo "↻ Exporting: $puml_file" + "$PLANTUML_PATH" -tsvg "$puml_file" + else + echo "✓ Up-to-date: $puml_file" + fi +} + +# Iterate over all .puml files +main() { + # Source - https://stackoverflow.com/a/9612232 + # Posted by Kevin, modified by community. See post 'Timeline' for change history + # Retrieved 2026-03-09, License - CC BY-SA 4.0 + find . -name '*.puml' -print0 | + while IFS= read -r -d '' line; do + export_file "$line" + done + + echo "✓ Exported all files" +} + +# Start script at entry point +main \ No newline at end of file From bce3326a0b8c16994f1e3896700af282a30842de Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Tue, 10 Mar 2026 17:29:37 +0100 Subject: [PATCH 19/31] Add: VSCode task to export all puml files to svgs with helper script --- .vscode/tasks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 101c28b..5c91f72 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,6 +6,12 @@ "type": "shell", "command": "./scripts/export-drawio.sh", "problemMatcher": [] + }, + { + "label": "Export puml to SVG", + "type": "shell", + "command": "./scripts/export-plantuml.sh", + "problemMatcher": [] } ] } \ No newline at end of file From a663286c0a183d411b95b706289bfee8a67c2048 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 11:27:14 +0100 Subject: [PATCH 20/31] Add: Exclude .idea folder --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3c559c3..aa33988 100644 --- a/.gitignore +++ b/.gitignore @@ -115,4 +115,7 @@ $RECYCLE.BIN/ # Exported images (optional – nur wenn du Exporte nicht versionieren willst) *.drawio.png *.drawio.svg -*.drawio.pdf \ No newline at end of file +*.drawio.pdf + +## IntelliJ +.idea \ No newline at end of file From 22990fa35e3b8c37e77e005b9de8e8f6811a3682 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 11:37:04 +0100 Subject: [PATCH 21/31] Add: Document outlining the core components and roles in the server-side networking --- .../docs/networking/server-architecture.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 documents/docs/networking/server-architecture.md diff --git a/documents/docs/networking/server-architecture.md b/documents/docs/networking/server-architecture.md new file mode 100644 index 0000000..93ad908 --- /dev/null +++ b/documents/docs/networking/server-architecture.md @@ -0,0 +1,81 @@ +# Server-Side Networking — How It All Fits Together + +This doc is meant to give a solid mixed-level understanding of how our server-side networking works. No deep dives — just a clear picture of what's happening under the hood when a client connects and sends a command. + +> **Note:** This is v1. Annotations and Reflection-based dispatching are on the roadmap but not covered here yet. + +> **Disclamer:** This document has been written by Claude. I modified certain parts and verified its contents for correctness. + +## Our Protocol at a Glance + +Our protocol is inspired by **POP3** - a classic, text-based protocol that communicates over a raw TCP connection. The idea is simple: the client sends a command as a plain-text string, and the server responds with either a success or an error. + +A typical exchange looks something like this: + +``` +Client → GET_DELTA SINCE=42 +Server → +OK + PLAYER_FOLDED playerId=3 + POT 240 + NEXT_TURN playerId=1 + . +``` + +Responses start with `+OK` on success or `-ERR` when something goes wrong. Commands are short, uppercase strings - sometimes followed by arguments. + +We don't use HTTP, there's no JSON body, no headers. Just a raw socket, a text stream, and a clearly defined set of commands. + +## Core Components & Their Roles + +Here's a quick rundown of the main building blocks: + +### `NetworkManager` +This is the entry point. It binds to a specific port and listens for incoming TCP connections. +Once a client connects, it creates a `Session` adds it to the `SessionManager` and goes back to waiting. Its not the responsibility of the `NetworkManager` to recieve and send data from and to each connected client. + +### `Session` +Every client that connects has its own `Session` instance running on its own thread. This component owns the lifecycle of that connection: it reads incoming data, passes it along for processing, and writes responses back. + +### `SessionManager` +The `SessionManager` stores all currently active sessions. It allows for the retrieval of a specific session by its id. + +### `ProtocolParser` +Raw text (encapsulated in a `RawRequest`) coming off the socket isn't immediately useful — the `ProtocolParser` turns it with the help of the `Tokenizer` into a `PrimitiveRequest`. + +### `RawRequest` +The `RawRequest` object is created after a message has been recieved. +It holds both a reference to the `Session` that recieved the message and the message itself. + +### `PrimitiveRequest` +The `PrimitiveRequest` object is created by the `ProtocolParser` after the content of the `RawRequest` has been tokenized by the `Tokenizer`. +The command is stored in its own attribute and the arguments are accessible as a dictionary. + +### `Tokenizer` +The job of the `tokenizer` is it, to take the raw string and turn it into tokens. +For example, the command `CHAT_LOBBY GAME=12 MESSAGE='All-in?'` will be decoded as `"CHAT_LOBBY", "GAME", "=", "12", "MESSAGE", "=", "All-in?"`. +It is unaware about the meaning. + +### `CommandParser` (per command) +Takes in the `PrimitiveRequest` and checks if all required fields are provided with the correct value. +Creates a command specific `Request` object containing command arguments in a structured manner. + +### `Request` (per command) +Has fields common along each implementation such as a reference to the `Session` that recieved the request. +Furthermore each implementation has fields unique to each command. For example, the `ChatLobbyRequest` has fields for the game and the message. + +### `CommandRouter` +By identifying the type of class of the `Request`, the `CommandRouter` routes the request to the matching `CommandHandler`. + +### `CommandHandler` (per command) +Each supported command has its own handler — a small, focused class that contains the logic for that specific command. They have access to the domain, containing inner parts of the game itself. + +### `Response` (per response) +The interface has two sub-interfaces for either failed `ErrorResponse` or successfull `SuccessResponse` execution of commands. + +Each step has exactly one responsibility. Increasing the ability to test and extend different components later on. + +## Concurrency - Handling Multiple Clients + +Every incoming connection spawns a new **Thread**. This means multiple clients can be served simultaneously without blocking each other. + +As each `Session` runs entirely on its own thread, there's no shared mutable state between sessions, with exception of the `SessionManager` and other key components explained later. From 32e4b04c87a31b288de398422ee7bed6baa9b9f8 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 11:43:27 +0100 Subject: [PATCH 22/31] Add: SessionId to identify each session --- documents/docs/networking/server-architecture.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documents/docs/networking/server-architecture.md b/documents/docs/networking/server-architecture.md index 93ad908..1be14eb 100644 --- a/documents/docs/networking/server-architecture.md +++ b/documents/docs/networking/server-architecture.md @@ -36,6 +36,9 @@ Once a client connects, it creates a `Session` adds it to the `SessionManager` a ### `Session` Every client that connects has its own `Session` instance running on its own thread. This component owns the lifecycle of that connection: it reads incoming data, passes it along for processing, and writes responses back. +### `SessionId` +The `SessionId` is used to uniquely identify each session. Essentially its a wrapper arround the UUID type. + ### `SessionManager` The `SessionManager` stores all currently active sessions. It allows for the retrieval of a specific session by its id. From fb6f14371ff191031ad56f33ae6ded8ccbbd33b9 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 11:49:46 +0100 Subject: [PATCH 23/31] Add: PlantUML diagramm outlining each component --- .../networking_components.puml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 documents/images/docs/networking/server-architecure/networking_components.puml diff --git a/documents/images/docs/networking/server-architecure/networking_components.puml b/documents/images/docs/networking/server-architecure/networking_components.puml new file mode 100644 index 0000000..647654a --- /dev/null +++ b/documents/images/docs/networking/server-architecure/networking_components.puml @@ -0,0 +1,96 @@ +@startuml +skinparam classAttributeIconSize 0 +skinparam packageStyle rectangle +skinparam linetype ortho +skinparam backgroundColor transparent + +' Network Layer +class NetworkManager { + + start(port: int) +} + +class Session { + - id: SessionId + + Session(id: SessionId) + + getId(): SessionId + + run() + + send(response: String) +} + +class SessionId { + - value: UUID + + SessionId() + + SessionId(value: UUID) +} + +class SessionManager { + - sessions: Map<UUID, Session> + + add(session: Session) + + getById(id: UUID): Session + + remove(id: UUID) +} + +NetworkManager --> Session : creates +NetworkManager --> SessionManager : adds session to +Session --> SessionId : identified by +Session --> RawRequest : creates +Session ..> Response : sends +SessionManager o-- Session : manages + +' Protocol Layer +class RawRequest { + - session: Session + - rawMessage: String +} + +class Tokenizer { + + tokenize(input: String): List<String> +} + +class ProtocolParser { + + parse(raw: RawRequest): PrimitiveRequest +} + +class PrimitiveRequest { + - command: String + - arguments: Map<String, String> +} + +RawRequest --> ProtocolParser : passed to +ProtocolParser --> Tokenizer : uses +ProtocolParser --> PrimitiveRequest : produces + +' Command Layer +interface CommandParser <<per command>> { + + parse(primitive: PrimitiveRequest): Request +} + +interface Request <<per command>> { + - session: Session +} + +class CommandRouter { + + route(request: Request) +} + +interface CommandHandler <<per command>> { + + handle(request: Request) +} + +PrimitiveRequest --> CommandParser : passed to +CommandParser --> Request : produces +CommandRouter --> CommandHandler : dispatches to +CommandRouter ..> Request : receives + +' Response Layer +interface Response { + + encode(): String +} + +interface SuccessResponse + +interface ErrorResponse + +Response <|-- SuccessResponse +Response <|-- ErrorResponse +@enduml From a5272b99ab44785de568ebf13f9376fc3f9a7c89 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 11:50:59 +0100 Subject: [PATCH 24/31] Add: Include diagramm in document --- documents/docs/networking/server-architecture.md | 2 ++ .../networking/server-architecure/networking_components.svg | 1 + 2 files changed, 3 insertions(+) create mode 100644 documents/images/docs/networking/server-architecure/networking_components.svg diff --git a/documents/docs/networking/server-architecture.md b/documents/docs/networking/server-architecture.md index 1be14eb..8537382 100644 --- a/documents/docs/networking/server-architecture.md +++ b/documents/docs/networking/server-architecture.md @@ -27,6 +27,8 @@ We don't use HTTP, there's no JSON body, no headers. Just a raw socket, a text s ## Core Components & Their Roles +![PlantUML diagramm of all components outlined in this document](/documents/images/docs/networking/server-architecure/networking_components.svg) + Here's a quick rundown of the main building blocks: ### `NetworkManager` diff --git a/documents/images/docs/networking/server-architecure/networking_components.svg b/documents/images/docs/networking/server-architecure/networking_components.svg new file mode 100644 index 0000000..9db9f6d --- /dev/null +++ b/documents/images/docs/networking/server-architecure/networking_components.svg @@ -0,0 +1 @@ +<?plantuml 1.2026.2?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="CLASS" height="1132px" preserveAspectRatio="none" style="width:820px;height:1132px;" version="1.1" viewBox="0 0 820 1132" width="820px" zoomAndPan="magnify"><defs/><g><!--class NetworkManager--><g class="entity" data-qualified-name="NetworkManager" data-source-line="7" id="ent0002"><rect fill="#F1F1F1" height="64.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="147.1445" x="257.37" y="7"/><ellipse cx="272.37" cy="23" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M274.8431,29.1431 Q274.2621,29.4419 273.6229,29.5913 Q272.9838,29.7407 272.2782,29.7407 Q269.7714,29.7407 268.4515,28.0889 Q267.1317,26.437 267.1317,23.3159 Q267.1317,20.1865 268.4515,18.5347 Q269.7714,16.8828 272.2782,16.8828 Q272.9838,16.8828 273.6312,17.0322 Q274.2787,17.1816 274.8431,17.4805 L274.8431,20.2031 Q274.2123,19.6221 273.6188,19.3523 Q273.0253,19.0825 272.3944,19.0825 Q271.0497,19.0825 270.3649,20.1492 Q269.6801,21.2158 269.6801,23.3159 Q269.6801,25.4077 270.3649,26.4744 Q271.0497,27.541 272.3944,27.541 Q273.0253,27.541 273.6188,27.2712 Q274.2123,27.0015 274.8431,26.4204 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="115.1445" x="286.37" y="28.291">NetworkManager</text><line style="stroke:#181818;stroke-width:0.5;" x1="258.37" x2="403.5145" y1="39" y2="39"/><line style="stroke:#181818;stroke-width:0.5;" x1="258.37" x2="403.5145" y1="47" y2="47"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106.5107" x="263.37" y="64.5352">+start(port: int)</text></g><!--class Session--><g class="entity" data-qualified-name="Session" data-source-line="11" id="ent0003"><rect fill="#F1F1F1" height="130.4414" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="175.2012" x="243.34" y="305.44"/><ellipse cx="301.2165" cy="321.44" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M303.6896,327.5831 Q303.1086,327.8819 302.4694,328.0313 Q301.8302,328.1807 301.1247,328.1807 Q298.6178,328.1807 297.298,326.5289 Q295.9782,324.877 295.9782,321.7559 Q295.9782,318.6265 297.298,316.9747 Q298.6178,315.3228 301.1247,315.3228 Q301.8302,315.3228 302.4777,315.4722 Q303.1252,315.6216 303.6896,315.9205 L303.6896,318.6431 Q303.0588,318.0621 302.4652,317.7923 Q301.8717,317.5225 301.2409,317.5225 Q299.8962,317.5225 299.2113,318.5892 Q298.5265,319.6558 298.5265,321.7559 Q298.5265,323.8477 299.2113,324.9144 Q299.8962,325.981 301.2409,325.981 Q301.8717,325.981 302.4652,325.7112 Q303.0588,325.4415 303.6896,324.8604 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="50.9482" x="321.7165" y="326.731">Session</text><line style="stroke:#181818;stroke-width:0.5;" x1="244.34" x2="417.5412" y1="337.44" y2="337.44"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93.6113" x="249.34" y="354.9752">-id: SessionId</text><line style="stroke:#181818;stroke-width:0.5;" x1="244.34" x2="417.5412" y1="361.9283" y2="361.9283"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="156.6934" x="249.34" y="379.4634">+Session(id: SessionId)</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="127.4971" x="249.34" y="395.9517">+getId(): SessionId</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="43.3398" x="249.34" y="412.44">+run()</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="163.2012" x="249.34" y="428.9283">+send(response: String)</text></g><!--class SessionId--><g class="entity" data-qualified-name="SessionId" data-source-line="19" id="ent0004"><rect fill="#F1F1F1" height="97.4648" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="174.3193" x="45.78" y="495.88"/><ellipse cx="96.7932" cy="511.88" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M99.2663,518.0231 Q98.6853,518.3219 98.0461,518.4713 Q97.407,518.6207 96.7014,518.6207 Q94.1946,518.6207 92.8747,516.9689 Q91.5549,515.317 91.5549,512.1959 Q91.5549,509.0665 92.8747,507.4147 Q94.1946,505.7628 96.7014,505.7628 Q97.407,505.7628 98.0544,505.9122 Q98.7019,506.0616 99.2663,506.3605 L99.2663,509.0831 Q98.6355,508.5021 98.042,508.2323 Q97.4485,507.9625 96.8176,507.9625 Q95.4729,507.9625 94.7881,509.0292 Q94.1032,510.0958 94.1032,512.1959 Q94.1032,514.2877 94.7881,515.3544 Q95.4729,516.421 96.8176,516.421 Q97.4485,516.421 98.042,516.1512 Q98.6355,515.8815 99.2663,515.3004 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="63.793" x="117.2932" y="517.171">SessionId</text><line style="stroke:#181818;stroke-width:0.5;" x1="46.78" x2="219.0993" y1="527.88" y2="527.88"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="86.3926" x="51.78" y="545.4152">-value: UUID</text><line style="stroke:#181818;stroke-width:0.5;" x1="46.78" x2="219.0993" y1="552.3683" y2="552.3683"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="84.0273" x="51.78" y="569.9034">+SessionId()</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="162.3193" x="51.78" y="586.3917">+SessionId(value: UUID)</text></g><!--class SessionManager--><g class="entity" data-qualified-name="SessionManager" data-source-line="25" id="ent0005"><rect fill="#F1F1F1" height="113.9531" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="231.2285" x="300.33" y="131.49"/><ellipse cx="356.9863" cy="147.49" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M359.4594,153.6331 Q358.8783,153.9319 358.2392,154.0813 Q357.6,154.2307 356.8945,154.2307 Q354.3876,154.2307 353.0678,152.5789 Q351.748,150.927 351.748,147.8059 Q351.748,144.6765 353.0678,143.0247 Q354.3876,141.3728 356.8945,141.3728 Q357.6,141.3728 358.2475,141.5222 Q358.8949,141.6716 359.4594,141.9705 L359.4594,144.6931 Q358.8285,144.1121 358.235,143.8423 Q357.6415,143.5725 357.0107,143.5725 Q355.6659,143.5725 354.9811,144.6392 Q354.2963,145.7058 354.2963,147.8059 Q354.2963,149.8977 354.9811,150.9644 Q355.6659,152.031 357.0107,152.031 Q357.6415,152.031 358.235,151.7612 Q358.8285,151.4915 359.4594,150.9104 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="109.416" x="377.4863" y="152.781">SessionManager</text><line style="stroke:#181818;stroke-width:0.5;" x1="301.33" x2="530.5585" y1="163.49" y2="163.49"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="219.2285" x="306.33" y="181.0252">-sessions: Map<UUID, Session></text><line style="stroke:#181818;stroke-width:0.5;" x1="301.33" x2="530.5585" y1="187.9783" y2="187.9783"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="155.9414" x="306.33" y="205.5134">+add(session: Session)</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="185.6572" x="306.33" y="222.0017">+getById(id: UUID): Session</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="126.1162" x="306.33" y="238.49">+remove(id: UUID)</text></g><!--class RawRequest--><g class="entity" data-qualified-name="RawRequest" data-source-line="35" id="ent0009"><rect fill="#F1F1F1" height="80.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="151.5693" x="255.16" y="504.13"/><ellipse cx="287.2029" cy="520.13" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M289.676,526.2731 Q289.095,526.5719 288.4558,526.7213 Q287.8166,526.8707 287.1111,526.8707 Q284.6042,526.8707 283.2844,525.2189 Q281.9646,523.567 281.9646,520.4459 Q281.9646,517.3165 283.2844,515.6647 Q284.6042,514.0128 287.1111,514.0128 Q287.8166,514.0128 288.4641,514.1622 Q289.1116,514.3116 289.676,514.6105 L289.676,517.3331 Q289.0452,516.7521 288.4517,516.4823 Q287.8581,516.2125 287.2273,516.2125 Q285.8826,516.2125 285.1977,517.2792 Q284.5129,518.3458 284.5129,520.4459 Q284.5129,522.5377 285.1977,523.6044 Q285.8826,524.671 287.2273,524.671 Q287.8581,524.671 288.4517,524.4012 Q289.0452,524.1315 289.676,523.5504 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81.6963" x="304.9902" y="525.421">RawRequest</text><line style="stroke:#181818;stroke-width:0.5;" x1="256.16" x2="405.7293" y1="536.13" y2="536.13"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118.4531" x="261.16" y="553.6652">-session: Session</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="139.5693" x="261.16" y="570.1534">-rawMessage: String</text><line style="stroke:#181818;stroke-width:0.5;" x1="256.16" x2="405.7293" y1="577.1066" y2="577.1066"/></g><!--class Response--><g class="entity" data-qualified-name="Response" data-source-line="36" id="ent0011"><rect fill="#F1F1F1" height="64.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="129.9336" x="484.98" y="512.37"/><ellipse cx="514.8788" cy="528.37" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M511.3065,524.6351 L511.3065,522.4769 L518.6859,522.4769 L518.6859,524.6351 L516.2206,524.6351 L516.2206,532.7118 L518.6859,532.7118 L518.6859,534.87 L511.3065,534.87 L511.3065,532.7118 L513.7718,532.7118 L513.7718,524.6351 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="64.8252" x="532.1896" y="533.661">Response</text><line style="stroke:#181818;stroke-width:0.5;" x1="485.98" x2="613.9136" y1="544.37" y2="544.37"/><line style="stroke:#181818;stroke-width:0.5;" x1="485.98" x2="613.9136" y1="552.37" y2="552.37"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="117.9336" x="490.98" y="569.9052">+encode(): String</text></g><!--class Tokenizer--><g class="entity" data-qualified-name="Tokenizer" data-source-line="45" id="ent0014"><rect fill="#F1F1F1" height="64.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="269.8857" x="7" y="786.08"/><ellipse cx="103.8311" cy="802.08" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M106.3042,808.2231 Q105.7231,808.5219 105.084,808.6713 Q104.4448,808.8207 103.7393,808.8207 Q101.2324,808.8207 99.9126,807.1689 Q98.5928,805.517 98.5928,802.3959 Q98.5928,799.2665 99.9126,797.6147 Q101.2324,795.9628 103.7393,795.9628 Q104.4448,795.9628 105.0923,796.1122 Q105.7397,796.2616 106.3042,796.5605 L106.3042,799.2831 Q105.6733,798.7021 105.0798,798.4323 Q104.4863,798.1625 103.8555,798.1625 Q102.5107,798.1625 101.8259,799.2292 Q101.1411,800.2958 101.1411,802.3959 Q101.1411,804.4877 101.8259,805.5544 Q102.5107,806.621 103.8555,806.621 Q104.4863,806.621 105.0798,806.3512 Q105.6733,806.0815 106.3042,805.5004 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="67.7236" x="124.3311" y="807.371">Tokenizer</text><line style="stroke:#181818;stroke-width:0.5;" x1="8" x2="275.8857" y1="818.08" y2="818.08"/><line style="stroke:#181818;stroke-width:0.5;" x1="8" x2="275.8857" y1="826.08" y2="826.08"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="257.8857" x="13" y="843.6152">+tokenize(input: String): List<String></text></g><!--class ProtocolParser--><g class="entity" data-qualified-name="ProtocolParser" data-source-line="49" id="ent0015"><rect fill="#F1F1F1" height="64.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="306.3896" x="133.75" y="653.35"/><ellipse cx="233.9033" cy="669.35" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M236.3765,675.4931 Q235.7954,675.7919 235.1563,675.9413 Q234.5171,676.0907 233.8115,676.0907 Q231.3047,676.0907 229.9849,674.4389 Q228.665,672.787 228.665,669.6659 Q228.665,666.5365 229.9849,664.8847 Q231.3047,663.2328 233.8115,663.2328 Q234.5171,663.2328 235.1646,663.3822 Q235.812,663.5316 236.3765,663.8305 L236.3765,666.5531 Q235.7456,665.9721 235.1521,665.7023 Q234.5586,665.4325 233.9277,665.4325 Q232.583,665.4325 231.8982,666.4992 Q231.2134,667.5658 231.2134,669.6659 Q231.2134,671.7577 231.8982,672.8244 Q232.583,673.891 233.9277,673.891 Q234.5586,673.891 235.1521,673.6212 Q235.7456,673.3515 236.3765,672.7704 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="97.583" x="254.4033" y="674.641">ProtocolParser</text><line style="stroke:#181818;stroke-width:0.5;" x1="134.75" x2="439.1396" y1="685.35" y2="685.35"/><line style="stroke:#181818;stroke-width:0.5;" x1="134.75" x2="439.1396" y1="693.35" y2="693.35"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="294.3896" x="139.75" y="710.8852">+parse(raw: RawRequest): PrimitiveRequest</text></g><!--class PrimitiveRequest--><g class="entity" data-qualified-name="PrimitiveRequest" data-source-line="53" id="ent0016"><rect fill="#F1F1F1" height="80.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="241.4277" x="312.23" y="777.83"/><ellipse cx="372.0513" cy="793.83" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M374.5244,799.9731 Q373.9434,800.2719 373.3042,800.4213 Q372.6651,800.5707 371.9595,800.5707 Q369.4527,800.5707 368.1328,798.9189 Q366.813,797.267 366.813,794.1459 Q366.813,791.0165 368.1328,789.3647 Q369.4527,787.7128 371.9595,787.7128 Q372.6651,787.7128 373.3125,787.8622 Q373.96,788.0116 374.5244,788.3105 L374.5244,791.0331 Q373.8936,790.4521 373.3001,790.1823 Q372.7066,789.9125 372.0757,789.9125 Q370.731,789.9125 370.0462,790.9792 Q369.3613,792.0458 369.3613,794.1459 Q369.3613,796.2377 370.0462,797.3044 Q370.731,798.371 372.0757,798.371 Q372.7066,798.371 373.3001,798.1012 Q373.8936,797.8315 374.5244,797.2504 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="113.2852" x="392.5513" y="799.121">PrimitiveRequest</text><line style="stroke:#181818;stroke-width:0.5;" x1="313.23" x2="552.6577" y1="809.83" y2="809.83"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="124.0723" x="318.23" y="827.3652">-command: String</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="229.4277" x="318.23" y="843.8534">-arguments: Map<String, String></text><line style="stroke:#181818;stroke-width:0.5;" x1="313.23" x2="552.6577" y1="850.8066" y2="850.8066"/></g><!--class CommandParser--><g class="entity" data-qualified-name="CommandParser" data-source-line="63" id="ent0020"><rect fill="#F1F1F1" height="73.1094" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="314.8047" x="275.54" y="918.81"/><ellipse cx="372.9316" cy="939.1205" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M369.3593,935.3857 L369.3593,933.2275 L376.7387,933.2275 L376.7387,935.3857 L374.2734,935.3857 L374.2734,943.4623 L376.7387,943.4623 L376.7387,945.6205 L369.3593,945.6205 L369.3593,943.4623 L371.8247,943.4623 L371.8247,935.3857 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="12" font-style="italic" lengthAdjust="spacing" textLength="93.0703" x="402.6572" y="935.4116">«per command»</text><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="111.5215" x="393.4316" y="951.478">CommandParser</text><line style="stroke:#181818;stroke-width:0.5;" x1="276.54" x2="589.3447" y1="959.4311" y2="959.4311"/><line style="stroke:#181818;stroke-width:0.5;" x1="276.54" x2="589.3447" y1="967.4311" y2="967.4311"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="302.8047" x="281.54" y="984.9663">+parse(primitive: PrimitiveRequest): Request</text></g><!--class Request--><g class="entity" data-qualified-name="Request" data-source-line="67" id="ent0021"><rect fill="#F1F1F1" height="73.1094" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="130.4531" x="411.72" y="1051.92"/><ellipse cx="430.9423" cy="1072.2305" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M427.37,1068.4957 L427.37,1066.3375 L434.7494,1066.3375 L434.7494,1068.4957 L432.2841,1068.4957 L432.2841,1076.5723 L434.7494,1076.5723 L434.7494,1078.7305 L427.37,1078.7305 L427.37,1076.5723 L429.8353,1076.5723 L429.8353,1068.4957 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="12" font-style="italic" lengthAdjust="spacing" textLength="93.0703" x="443.8805" y="1068.5216">«per command»</text><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="54.3252" x="463.2531" y="1084.588">Request</text><line style="stroke:#181818;stroke-width:0.5;" x1="412.72" x2="541.1731" y1="1092.5411" y2="1092.5411"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118.4531" x="417.72" y="1110.0763">-session: Session</text><line style="stroke:#181818;stroke-width:0.5;" x1="412.72" x2="541.1731" y1="1117.0294" y2="1117.0294"/></g><!--class CommandRouter--><g class="entity" data-qualified-name="CommandRouter" data-source-line="71" id="ent0022"><rect fill="#F1F1F1" height="64.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="182.6729" x="625.61" y="923.12"/><ellipse cx="656.8592" cy="939.12" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M659.3324,945.2631 Q658.7513,945.5619 658.1121,945.7113 Q657.473,945.8607 656.7674,945.8607 Q654.2606,945.8607 652.9408,944.2089 Q651.6209,942.557 651.6209,939.4359 Q651.6209,936.3065 652.9408,934.6547 Q654.2606,933.0028 656.7674,933.0028 Q657.473,933.0028 658.1204,933.1522 Q658.7679,933.3016 659.3324,933.6005 L659.3324,936.3231 Q658.7015,935.7421 658.108,935.4723 Q657.5145,935.2025 656.8836,935.2025 Q655.5389,935.2025 654.8541,936.2692 Q654.1693,937.3358 654.1693,939.4359 Q654.1693,941.5277 654.8541,942.5944 Q655.5389,943.661 656.8836,943.661 Q657.5145,943.661 658.108,943.3912 Q658.7015,943.1215 659.3324,942.5404 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="114.5635" x="674.4702" y="944.411">CommandRouter</text><line style="stroke:#181818;stroke-width:0.5;" x1="626.61" x2="807.2829" y1="955.12" y2="955.12"/><line style="stroke:#181818;stroke-width:0.5;" x1="626.61" x2="807.2829" y1="963.12" y2="963.12"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="170.6729" x="631.61" y="980.6552">+route(request: Request)</text></g><!--class CommandHandler--><g class="entity" data-qualified-name="CommandHandler" data-source-line="75" id="ent0023"><rect fill="#F1F1F1" height="73.1094" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="192.3867" x="620.75" y="1051.92"/><ellipse cx="652.6852" cy="1072.2305" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M649.1129,1068.4957 L649.1129,1066.3375 L656.4923,1066.3375 L656.4923,1068.4957 L654.027,1068.4957 L654.027,1076.5723 L656.4923,1076.5723 L656.4923,1078.7305 L649.1129,1078.7305 L649.1129,1076.5723 L651.5783,1076.5723 L651.5783,1068.4957 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="12" font-style="italic" lengthAdjust="spacing" textLength="93.0703" x="685.2899" y="1068.5216">«per command»</text><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="122.7529" x="670.4486" y="1084.588">CommandHandler</text><line style="stroke:#181818;stroke-width:0.5;" x1="621.75" x2="812.1367" y1="1092.5411" y2="1092.5411"/><line style="stroke:#181818;stroke-width:0.5;" x1="621.75" x2="812.1367" y1="1100.5411" y2="1100.5411"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="180.3867" x="626.75" y="1118.0763">+handle(request: Request)</text></g><!--class SuccessResponse--><g class="entity" data-qualified-name="SuccessResponse" data-source-line="89" id="ent0028"><rect fill="#F1F1F1" height="48" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="149.4688" x="475.21" y="661.59"/><ellipse cx="490.21" cy="677.59" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M486.6377,673.8551 L486.6377,671.6969 L494.0171,671.6969 L494.0171,673.8551 L491.5518,673.8551 L491.5518,681.9318 L494.0171,681.9318 L494.0171,684.09 L486.6377,684.09 L486.6377,681.9318 L489.1031,681.9318 L489.1031,673.8551 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="117.4688" x="504.21" y="682.881">SuccessResponse</text><line style="stroke:#181818;stroke-width:0.5;" x1="476.21" x2="623.6788" y1="693.59" y2="693.59"/><line style="stroke:#181818;stroke-width:0.5;" x1="476.21" x2="623.6788" y1="701.59" y2="701.59"/></g><!--class ErrorResponse--><g class="entity" data-qualified-name="ErrorResponse" data-source-line="91" id="ent0029"><rect fill="#F1F1F1" height="48" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="130.1982" x="659.84" y="661.59"/><ellipse cx="674.84" cy="677.59" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M671.2677,673.8551 L671.2677,671.6969 L678.6471,671.6969 L678.6471,673.8551 L676.1818,673.8551 L676.1818,681.9318 L678.6471,681.9318 L678.6471,684.09 L671.2677,684.09 L671.2677,681.9318 L673.7331,681.9318 L673.7331,673.8551 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="98.1982" x="688.84" y="682.881">ErrorResponse</text><line style="stroke:#181818;stroke-width:0.5;" x1="660.84" x2="789.0382" y1="693.59" y2="693.59"/><line style="stroke:#181818;stroke-width:0.5;" x1="660.84" x2="789.0382" y1="701.59" y2="701.59"/></g><!--link NetworkManager to Session--><g class="link" data-entity-1="ent0002" data-entity-2="ent0003" data-link-type="dependency" data-source-line="32" id="lnk6"><path codeLine="32" d="M278.85,71.93 C278.85,125.9 278.85,228.67 278.85,299" fill="none" id="NetworkManager-to-Session" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="278.85,305,282.85,296,278.85,300,274.85,296,278.85,305" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="45.1318" x="232.85" y="185.0384">creates</text></g><!--link NetworkManager to SessionManager--><g class="link" data-entity-1="ent0002" data-entity-2="ent0005" data-link-type="dependency" data-source-line="33" id="lnk7"><path codeLine="33" d="M352.42,71.62 C352.42,89.02 352.42,104.99 352.42,125.17" fill="none" id="NetworkManager-to-SessionManager" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="352.42,131.17,356.42,122.17,352.42,126.17,348.42,122.17,352.42,131.17" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="98.1792" x="253.42" y="97.9684">adds session to</text></g><!--link Session to SessionId--><g class="link" data-entity-1="ent0003" data-entity-2="ent0004" data-link-type="dependency" data-source-line="34" id="lnk8"><path codeLine="34" d="M249.25,436.1 C249.25,468.19 249.25,500.03 249.25,500.03 C249.25,500.03 243.02,500.03 226.5,500.03" fill="none" id="Session-to-SessionId" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="220.5,500.03,229.5,504.03,225.5,500.03,229.5,496.03,220.5,500.03" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="78.9204" x="170.25" y="479.0084">identified by</text></g><!--link Session to RawRequest--><g class="link" data-entity-1="ent0003" data-entity-2="ent0009" data-link-type="dependency" data-source-line="35" id="lnk10"><path codeLine="35" d="M330.94,435.93 C330.94,458.68 330.94,477.55 330.94,497.75" fill="none" id="Session-to-RawRequest" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="330.94,503.75,334.94,494.75,330.94,498.75,326.94,494.75,330.94,503.75" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="45.1318" x="284.94" y="466.4084">creates</text></g><!--link Session to Response--><g class="link" data-entity-1="ent0003" data-entity-2="ent0011" data-link-type="dependency" data-source-line="36" id="lnk12"><path codeLine="36" d="M412.64,436.09 C412.64,485.56 412.64,545.03 412.64,545.03 C412.64,545.03 443.14,545.03 478.51,545.03" fill="none" id="Session-to-Response" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#181818" points="484.51,545.03,475.51,541.03,479.51,545.03,475.51,549.03,484.51,545.03" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="36.7466" x="413.64" y="523.0584">sends</text></g><!--reverse link SessionManager to Session--><g class="link" data-entity-1="ent0005" data-entity-2="ent0003" data-link-type="aggregation" data-source-line="37" id="lnk13"><path codeLine="37" d="M359.44,257.61 C359.44,276.5 359.44,285.66 359.44,305.13" fill="none" id="SessionManager-backto-Session" style="stroke:#181818;stroke-width:1;"/><polygon fill="none" points="359.44,245.61,355.44,251.61,359.44,257.61,363.44,251.61,359.44,245.61" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="56.5386" x="302.44" y="271.9384">manages</text></g><!--link RawRequest to ProtocolParser--><g class="link" data-entity-1="ent0009" data-entity-2="ent0015" data-link-type="dependency" data-source-line="58" id="lnk17"><path codeLine="58" d="M330.94,585.39 C330.94,606.86 330.94,626.83 330.94,646.95" fill="none" id="RawRequest-to-ProtocolParser" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="330.94,652.95,334.94,643.95,330.94,647.95,326.94,643.95,330.94,652.95" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="61.001" x="268.94" y="615.7384">passed to</text></g><!--link ProtocolParser to Tokenizer--><g class="link" data-entity-1="ent0015" data-entity-2="ent0014" data-link-type="dependency" data-source-line="59" id="lnk18"><path codeLine="59" d="M205.32,718.06 C205.32,738.63 205.32,759.14 205.32,779.73" fill="none" id="ProtocolParser-to-Tokenizer" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="205.32,785.73,209.32,776.73,205.32,780.73,201.32,776.73,205.32,785.73" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="28.5645" x="176.32" y="748.4584">uses</text></g><!--link ProtocolParser to PrimitiveRequest--><g class="link" data-entity-1="ent0015" data-entity-2="ent0016" data-link-type="dependency" data-source-line="60" id="lnk19"><path codeLine="60" d="M376.18,718.06 C376.18,736.02 376.18,752.51 376.18,771.63" fill="none" id="ProtocolParser-to-PrimitiveRequest" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="376.18,777.63,380.18,768.63,376.18,772.63,372.18,768.63,376.18,777.63" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="58.2651" x="317.18" y="744.4084">produces</text></g><!--link PrimitiveRequest to CommandParser--><g class="link" data-entity-1="ent0016" data-entity-2="ent0020" data-link-type="dependency" data-source-line="79" id="lnk24"><path codeLine="79" d="M432.94,859.07 C432.94,878.01 432.94,894.35 432.94,912.74" fill="none" id="PrimitiveRequest-to-CommandParser" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="432.94,918.74,436.94,909.74,432.94,913.74,428.94,909.74,432.94,918.74" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="61.001" x="370.94" y="885.4784">passed to</text></g><!--link CommandParser to Request--><g class="link" data-entity-1="ent0020" data-entity-2="ent0021" data-link-type="dependency" data-source-line="80" id="lnk25"><path codeLine="80" d="M476.94,992.09 C476.94,1010.71 476.94,1027.26 476.94,1045.87" fill="none" id="CommandParser-to-Request" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="476.94,1051.87,480.94,1042.87,476.94,1046.87,472.94,1042.87,476.94,1051.87" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="58.2651" x="417.94" y="1018.5484">produces</text></g><!--link CommandRouter to CommandHandler--><g class="link" data-entity-1="ent0022" data-entity-2="ent0023" data-link-type="dependency" data-source-line="81" id="lnk26"><path codeLine="81" d="M716.94,987.93 C716.94,1007.17 716.94,1025.59 716.94,1045.55" fill="none" id="CommandRouter-to-CommandHandler" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="716.94,1051.55,720.94,1042.55,716.94,1046.55,712.94,1042.55,716.94,1051.55" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="84.3477" x="631.94" y="1016.3084">dispatches to</text></g><!--link CommandRouter to Request--><g class="link" data-entity-1="ent0022" data-entity-2="ent0021" data-link-type="dependency" data-source-line="82" id="lnk27"><path codeLine="82" d="M625.21,955.03 C613.52,955.03 605.55,955.03 605.55,955.03 C605.55,955.03 605.55,1088.03 605.55,1088.03 C605.55,1088.03 580.15,1088.03 548.38,1088.03" fill="none" id="CommandRouter-to-Request" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#181818" points="542.38,1088.03,551.38,1092.03,547.38,1088.03,551.38,1084.03,542.38,1088.03" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="50.8193" x="554.55" y="1039.8584">receives</text></g><!--reverse link Response to SuccessResponse--><g class="link" data-entity-1="ent0011" data-entity-2="ent0028" data-link-type="extension" data-source-line="93" id="lnk30"><path codeLine="93" d="M549.94,595.31 C549.94,621.01 549.94,638.24 549.94,661.44" fill="none" id="Response-backto-SuccessResponse" style="stroke:#181818;stroke-width:1;"/><polygon fill="none" points="549.94,577.31,543.94,595.31,555.94,595.31,549.94,577.31" style="stroke:#181818;stroke-width:1;"/></g><!--reverse link Response to ErrorResponse--><g class="link" data-entity-1="ent0011" data-entity-2="ent0029" data-link-type="extension" data-source-line="94" id="lnk31"><path codeLine="94" d="M633.11,545.03 C682.86,545.03 724.94,545.03 724.94,545.03 C724.94,545.03 724.94,620.84 724.94,661.11" fill="none" id="Response-backto-ErrorResponse" style="stroke:#181818;stroke-width:1;"/><polygon fill="none" points="615.11,545.03,633.11,551.03,633.11,539.03,615.11,545.03" style="stroke:#181818;stroke-width:1;"/></g><?plantuml-src VLNRRXiX47tVh-2jQpKRzNbbMMhJIhMKL57SV01P9Wxo6hO3cyYz_7i75bXOkxLaIp1dpfmv0zWSf1euyYFhUcxCHsjHFeqMjfrME_aJs8VA9CZ0kmFVmywUUc08dULgtqC6wAK2Ung0QRGlEWiyKUOUzQZ4ZUurCejS6Gg1ibNrZdq3-wRnm6xv2R3oIkBU7LTK4jclYh7tp5YEjXw8lcLIsLNrfmhm7HWZjVAuXadHnfsjy9bXLHUHbG_jmMv5lLfcuAZg2M10YHh135eP81XvfFRdjRSYL7_b_KYunyVjvxosAx9OPyXplhprXaJuJTEoEpwiNSPL16uyAnUY3g3KIchmqucgkTvzgHIV6eMZVeKvweGit6-QJNBOVLhM8N0BvZ_0kDKwPIRgPrPNaIa3atVfGwCLT33aimJ1dau5_86_FS2F4OpDu55B15vV4p2CY-MyRf095lNffbdqTlGHuqxcFMghEzqNHpCJKSmcjxLXoD_kQ4bCySZC4_wk3w3eUiLpRSEwfWipsdJ4MdOhZLrFgysS7bNTSpI9Ot2BcggscK2Yk4Tvb5Q-GjZAQSf8wARJHt909DLkZ-D-FD9Gml6R8bSiKvQPuWQra4Yk3bII1ahpNyGSV3R4GKSoz1BgJ32neXPZDmthPX8UPaNl0-0pxo3k1wBrUg3VqEHcKxWtn0Bdjf6JcO4pUPHoYVR2oKZc1r4FcbxRE49q2tffF6EgjoeBXiIlzEt_sSwBZrwcEdFHMLjwb4-hZFXBcD_0R0HbInbhrDeo8UdPjzqBc8mw94pNjg2cFnaWeNwwwKOlnvi2K-kWEYt0l-VfritWtTYHL1Dpyj0NH8rp8D6kVxiNOf5NHelKlm00?></g></svg> \ No newline at end of file From 3c9f19c5dad5b42c9a2a864d9d5c4483a4d141ca Mon Sep 17 00:00:00 2001 From: Jona Walpert <jona.walpert@stud.unibas.ch> Date: Wed, 11 Mar 2026 13:13:42 +0100 Subject: [PATCH 25/31] Add: Game logo, placeholder abckground image --- src/main/resources/images/backround.png | Bin 0 -> 4917 bytes src/main/resources/images/logo.png | Bin 0 -> 3121 bytes src/main/resources/images/logoinverted.png | Bin 0 -> 198316 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/images/backround.png create mode 100644 src/main/resources/images/logo.png create mode 100644 src/main/resources/images/logoinverted.png diff --git a/src/main/resources/images/backround.png b/src/main/resources/images/backround.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e1841521b60a35d641455cf7ad2731329e2198 GIT binary patch literal 4917 zcmeAS@N?(olHy`uVBq!ia0y~yU}<1rVCvvt1B!5HH}V20#^NA%Cx&(BWL^R}Ea{HE zjtmSN`?>!lvI6;>1s;*b3=DjSL74G){)!Z!hW5;mh>{3jAFJg2T)o7U{G?R9irfOA zY6b=y`-+0Z<m}WEg{0K<%sgB5_q+G2D`ckFs)w8U8u%7Cr)Fe&R28KLSNVk`S7j$# zrl{N7@p9QzSOHDQO)W`OsL0L9E4HezRRSAq1!RMS^_A>wic-?7f?V97Du6s&rHqo2 z0xNy}^73-Ma$~*xqI7*jOG`_A10#JSBVC{h-Qvo;lEez#ykcdj0WPV<$wiq3C7Jno z3Lpa$lk!VTY?Vq&GgGY664OkRQc_HHlM;<nbPWwt5_J<(j16^DjEz&wO${xK(hQT7 zpw^dUrXlRlD~1LGFhKM{GWv#k1|TPc*jD~UnW;cmfULALv;oVbh}r0a9D?L9JBSF- zuRs<&WI(|L58c!}V33vr!%k<)3~mMnL32+R$B>G+w>Jz04=6AkI1nV;^P{|t?Z(6j zzvYz`lh2phfnsOVKXVXuA&G$zNF9-M0Pzah1c0Q&97Yx(C6GQUH5wYDX=5}yj1~@~ z<>6@UFj^swHV;NyM58UD(H7BYi)ge(G}<B>Z4nKt7LmByA9kD2J#|vRcxCW(^>bP0 Hl+XkK_IHxK literal 0 HcmV?d00001 diff --git a/src/main/resources/images/logo.png b/src/main/resources/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e906bd477ab5f700d7b1d62575ac4cfb9e9bf110 GIT binary patch literal 3121 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 a$AP*V*dH?OiofV(K0rF;O(_3~a`nI3SIF4_ literal 0 HcmV?d00001 diff --git a/src/main/resources/images/logoinverted.png b/src/main/resources/images/logoinverted.png new file mode 100644 index 0000000000000000000000000000000000000000..73053b6241f54d33d8d5422e05dd44236443b79d GIT binary patch literal 198316 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 literal 0 HcmV?d00001 From 7daae485da714390df8b88ddc0a0050273fd472c Mon Sep 17 00:00:00 2001 From: Jona Walpert <jona.walpert@stud.unibas.ch> Date: Wed, 11 Mar 2026 13:22:33 +0100 Subject: [PATCH 26/31] Chore: Added javafx modules to build.gradle file --- .vscode/settings.json | 3 +++ bin/main/images/backround.png | Bin 0 -> 4917 bytes bin/main/images/logo.png | Bin 0 -> 3121 bytes bin/main/images/logoinverted.png | Bin 0 -> 198316 bytes build.gradle | 2 +- 5 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 bin/main/images/backround.png create mode 100644 bin/main/images/logo.png create mode 100644 bin/main/images/logoinverted.png diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/bin/main/images/backround.png b/bin/main/images/backround.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e1841521b60a35d641455cf7ad2731329e2198 GIT binary patch literal 4917 zcmeAS@N?(olHy`uVBq!ia0y~yU}<1rVCvvt1B!5HH}V20#^NA%Cx&(BWL^R}Ea{HE zjtmSN`?>!lvI6;>1s;*b3=DjSL74G){)!Z!hW5;mh>{3jAFJg2T)o7U{G?R9irfOA zY6b=y`-+0Z<m}WEg{0K<%sgB5_q+G2D`ckFs)w8U8u%7Cr)Fe&R28KLSNVk`S7j$# zrl{N7@p9QzSOHDQO)W`OsL0L9E4HezRRSAq1!RMS^_A>wic-?7f?V97Du6s&rHqo2 z0xNy}^73-Ma$~*xqI7*jOG`_A10#JSBVC{h-Qvo;lEez#ykcdj0WPV<$wiq3C7Jno z3Lpa$lk!VTY?Vq&GgGY664OkRQc_HHlM;<nbPWwt5_J<(j16^DjEz&wO${xK(hQT7 zpw^dUrXlRlD~1LGFhKM{GWv#k1|TPc*jD~UnW;cmfULALv;oVbh}r0a9D?L9JBSF- zuRs<&WI(|L58c!}V33vr!%k<)3~mMnL32+R$B>G+w>Jz04=6AkI1nV;^P{|t?Z(6j zzvYz`lh2phfnsOVKXVXuA&G$zNF9-M0Pzah1c0Q&97Yx(C6GQUH5wYDX=5}yj1~@~ z<>6@UFj^swHV;NyM58UD(H7BYi)ge(G}<B>Z4nKt7LmByA9kD2J#|vRcxCW(^>bP0 Hl+XkK_IHxK literal 0 HcmV?d00001 diff --git a/bin/main/images/logo.png b/bin/main/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e906bd477ab5f700d7b1d62575ac4cfb9e9bf110 GIT binary patch literal 3121 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 a$AP*V*dH?OiofV(K0rF;O(_3~a`nI3SIF4_ literal 0 HcmV?d00001 diff --git a/bin/main/images/logoinverted.png b/bin/main/images/logoinverted.png new file mode 100644 index 0000000000000000000000000000000000000000..73053b6241f54d33d8d5422e05dd44236443b79d GIT binary patch literal 198316 zcmeI534Bv!y2jI;wv<Iit%ZTm;#I`7X}ZwF7OX8oFIEvz5g8>-(l*-dwt*HM#9JM4 z1yOW#Mjb~s(ZOZZkr_7-0TEZOTu~eqaA6c>P?jpC%=4v9TWXQCIVU;G6MipAPR{wh z_x-=`bGClxoLeTHKRzKYDb8RpBos|3oQ(I6h4qQX|AVK!^S^l8aLtsdp2_xlV@+*! zsk_2u^vtet8C?zTQiGx4qsmF!64sjgG?(2vf5z(mi5uLLR}A(vmlQlQ;*Fv)j(!h@ zZN2W({x3%N$@qNplB-s3yfbUm%=i)3pPc8-{{FR}`XxL$J16q5zuNXz%c7+H*ADF; z@yb8Oec>Eozj4#W?+$6H&A4?zgQsxuI`d|8Np0BLhYw9%@z}FJ^x1Up69dMYCwLB6 z-kkozOLfnDyK-HN{gFEs-kn~zKWXUl@cQ_MzfK!o`^>8Q-Zy<R{<+`fe(R2D4jYhk zS@XKH$89qh!ftdI6ig~ADDZ`ZKpvR0@Rf;c&mOYw%8Qnsabnb2H|IDXJ%84U<oQc) z9rx6vkK$I^?wkHo{2ww$j7**1c=eh!!*07c=H#;9{Cav+{=Z{xo_l@H$bU?@<<p6A zrjp#8@7BI>?SdETuYdP1mMyz)345&clz$aYfAfn=*R3AaylaE`kpZWce!cO{l9L|V z`OVNVFMl$w;ogIPAG_h<$>vi&8+41~ywPXwNZ9dV@x-rQykJO)t7!O`(;l+yYJ6?M z^xyyHx}DQ&r+#+-@RUD3bXvcGw=GS*_l5iJNw}xsrkuY#n)F4y_4g;fefa}hu6ppf zJsYn~JhOb%DNp?0kn!OYCnQG=w0!;O!*THgr)5n(Z;5;CciV^6HIJ;_`1xbkFLZ=u zJ$mPmu}#C8zx)3)&maHKB{PQXn)CFx3u28=#Xp<$x5INPwyhkr`bgjD?qAPc8T;m( zyFS@fzCC3|`QTNnzAOKJXwBavZXffnyM|lu`qQ#WXNK=7+gCh$#^)y`POZE4>ODL6 zg}pxc(oLgRuNiK>?2+9w7t|k|dGNrV`)AD^yQQ}F?WZQbe<ez9mK!BDwfH=nqq;KH z?yN3xr8ZR7p!N)g+_4QccE{B&kFms6=B~<1-t)pM$ws#`FZt4}Vsmj#fvem-;hI|4 zMc14^#c|Em4y!YH?3lRR1{)fvbb0K?hRTYnI$J|tvbS9uK8wj|$wu!io~!ecrxs5# z7F5@|jG3vKsb<r-2KOvW@|ZYdZmqM_Ho5TJwkGf`FS*>~sj;P{)z{aj)@P(v*OsNF zTdmeKvn9=9F<}l<-Rvrly}?vfmm->IZKKdt=cskpc-+-hM$x9dq<W?&FF6^%8-4y& z))W`FH(yoPMgct18tgS`>8a+l%F49POVoMB%|at>4t2Xk-4rM_ZL+JbdS<P|HEx!x z%9GN$38$ld`I?!v72a^1jx<+=s}i%;q1W`Ix*T6rJgI#NfuPJ?S>s&_u#cM4<1X!h zb(C-77jHP79l_%5?H)C^FLv+Dn5($hR#@$rDcmb6%u5#i+nm)7x6|hR=*qT~W;;sE zrVNMKY07k3tR|}^+ht16E_IY-m1JdFoUTqy6;;)F>{SkzXbO#|y3tOS)m~~gXJ?t5 zIgT7tX4YuC$?C|lm@FBYS>_B^y33WB-l>TTYTc+xdqrojL{m;QRhsFtWTa<in?{#f zvQ3#8t}K(a)P=83ha<<Fkz+Btvb{|?9kz3;Yb)(Yr@PW#=1QxnD)aszfZOsX73C#c zQq3JxCRNxyrC0zOa9262>+3pxn&Pf>UF5L~p6R1UXP8H4rDs?(N9S16tsOI6;;OAf zEs9pt&8Zo!PKd<V5Dw7VMWvzv?-Gc{R#5A*d#Y=vR99EzB@3sFV#)TOiy<hd-D5Ab zdt7MLY{|BnGi+w-l=RWIEQ>8YYm_<NX6`h9wbNZX`*?E;`51FslRm*+hyG`KC$(zo zBG>=4erx?&;r5EjX!Od$W_PrPP-maza(ewlTdhAi%I#HUF04P=3f4E>-CZ%b%;pkn zX{o8ij-tx6mSmf(8Te#%WLUDZoFx{!wH01_>vh$oo_c$&E58gJ!3qWHWo10QEmb3r z_NKnvCGr4dCUb_V1DQ2Btu5FzvBvO4o16AC9=YBbI;~=`T<bKfy09ipYhM+%@q({% zzS{C2XBS2zJLwT$;v%jd5$Ms(tS;i}5rH1f%<7U1S6s)zl&cDRK=n9OTD1A4ojA;j zE}1yK&~QZjd-$d0cVkLy&4kP942Jj<goSPX=XK4P7~?4_9vAa<MC^dfK_vtKHr`+` z8j1?@r!<5a);C(`U=F@WfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14c)JkCg-SZ#!jRjC^4D_1>NPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNaOgFs&Y=P%$`fDRt~L;@s00wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JN+l2;78X_k=Zs2qkbaSXW(Wwe!J5Lu!<XYdK{J6d5E4)t0SIg&>}&XS z1n-9+x*L?{qiX$W*_@Teu>h(~pRA5UY@=YC(a8~?;rrf*h=?m>?U;>|fDQ=^gt(qT z-0foXea3V=6xEQcLnaKF1f(YbLB&KyMlL|hKNFj88s>QsHca~7*&+$(i$DZ~R0i9J zu#YJ?@f(iq9>9F@`ohC#NkBpZ5KsYZJDU7OvH5<-@!Yi%cF(3rKtBW^v=nT$-a*8E ziA~Ih`S)YFY5JkWNJ&5%0(~Kz=g^ejV)M<5<#xbq(zwA^NI(Y!Aeco6v&UldErTuH zMtmNs15^x=1Og*~<FHByt_iL5T5w`nw6PrTF@br*rS(SOuEk${r^~Sb5X`yQQtd** z0}`HC9&I(Ftwwx~(pv#3Hs1A();WrGPLGbo#KfEqmAr@816G``4JdCnOq^8-(<4*H zOak2!fGAFa*q+9Zl8MdN7TW#*HoDu6Igtc(NFWY^xE<Dv*d-U7XcJ<773~k!AzOw_ z0!I;m4#FI;?=e*tvH99XA4_07t8x^6e$f>H2w)8C8=olTd`CZrVd6{*UnHPE0<p2N z!?3Hj6_LxjW$WBN1Sf{3Ow}K4MyzWBDEfHVs?K5v)@O)(C;HCPbpjZ?t_Vb<=obb{ zVBPft!Mz?A7dJ##)ETTk382{B5ZuA;gwXA@leQPH?HCIn%kC%qS&F^~>N6vZS{DRx zmFWbCPm-Y}02YuC;m1MPbugaA)J65_EXe5S=u~V~-VIbJQZ5g%iBTz+=qwcsSL*~& z)Th9nkWwsCwFSS#)vp;^&jI7p2>}#zENmW3woxTO8PM(}w!Gq6pG2Lcgkfr#0EBjh zlEnu0Ua`W*WiTGkX}K7+9vccc5B9CEsFnVX3-!e1B;i`m3**xu0SGMxwgsUDw7VC~ z<*{qYQ7Ia%Ma_o<p$$Uu_UeWu!4z0`eZg<uFh%nfs;vMcQM4f)Rnnc1dNK|Eufs~? zR3)Wl>4KZA9b*BM<QaWY3;`SyRiSYGA7P@v-A{<G1@>pL)m98xIzj?n37|m5BXPdO zbk!Wg9m`k5@gr6qv+;Xm*Eso|1VT;#1!{tAf+MOIn=b|kP7DRRFywIQ010#<@GBJN z3QW_C*nF{I*HT>OW9bqvzmh=62w;42<W1;Zb7J$w1i`I`C54PIJ0<}i0TiMWTY*FP zu35o}*bwJp7$4)~OFZ9$PXGm20P{XKSQMoW?E2tSW{)HQ0ub71*s{Hkk9rWAH%7#} z4<}M)AZETuAgBcTqxhajkGc_?FIKE*K7r*2l{P!`5ooH9FW2>00MuUCB3O&BAa(e@ z8u6$2DuVAMAQypfF{%Vxy8Y-tcU!v7F@wQ_5M6>?pxG`7h+{Z$uM)qX8{D}-wfGq; zpL)c_%Y3K}SqPjCv570Db-iWl96z>lKj8btve0Iux+Wk_m?-jICCExhR{&TEeG1Fc zb#*X!NeP6bpza8DVJo?R@Yw+4s1!-dPN$8bkji0AN-lUM_y$HRV7ym~PIV$l*eIZL zV7rx2&`S3KyWEFi^CSV!M)XGjLQ6pbeW3J0SCW5Vwg+a1PhtA27)IP{0t4`Z*!+J2 z&fNzVf7me=AfzWT5VT_YhxoR_&gyj{xS+lWBqk<`M^7z;DfZ^BN)lLizJT4!m_Afr zmE%XW>l>}&UWI&hA%H*bMz~Qmdof)xn+`Sl0)~Hr-+jCA<yR8uAb=tf*W&EJ6pAfC z2I8y=R$jjaOCI2Llo&tx2*kw1oQfiO58o*^i5Rr)DR5|tugLh`PCyJTd6r_6eq{sE zeF5VQQ`$?7AG||LP!xK;{zlfVI#y<Hf$zyIH4+e)1c)1!@U)~vyOAiVZp916kK?yE zJ(paBo`V;$Lu_Jb$sbx;T6pIi`HLY1ti=hGb+E(ocgG$SMF1yBMk4>Zx?u?t(CyYo zg6TK6ocpA2EP!8qV2k$+_)k+*QP3Ir2_Wf7uxBJFI=>2y{d9;PqWc)^bL7u5dr%62 zI3)cJB>g7}p+gA4WC8YTV`F1EG({<jkkbz&+XdSf%mQOyKNmJudXIpMu12Nk@dV1* z5ZnDnjvN^vr>NPsUj)uXE)HPM*Zi8BGpUOJk}J-bZHCu8?otQ?Y~_ALPWTufbxFxD zZwDZ;Pf={jTiGBlUq(emW%!jv&ZG(g5lHIo6r1wI=F17TbZ_7@Pok(oPRDT)Vsj&* z995!_-AJFvy1pYv%aE%Rj+0JR|E&3wCP?6aBSQ;ul0;W$!&F+8{{_+ggj|VHDPoMP z3SVPJMd9x7Mnh3y{uC9uFQLn3i0>V=#}g(hVF2;%#kF;pNmv7HDi{PHw3A`H!>dY! z)_J7}(S3#!Dx-r@C+tHS0#Oj#Eihh&qe8(6@gaAw!iGpw4s1nE0?1QX3D%Mv!lhEN z3Bd_N9>rsPG<9`{=1!m>LHV$IaT!j3IaSTJgF+x3y@;oZa8ycAaQCni2<=oDPm-v7 z%Z9YKpX5S555lJQuoO7E+ys!6e%OuVy-GS2Yv;mM^JnCC2P{wS8e;oBAONu~Qgcz! z_l~VvcPt>db+8dVC<)Fg7Xkd;rWVOK*j=JH?UxH#EqsvQ7OYgor9G;C7P9J3nnXrM z7UKlTO&F;#5Xm^^>kM-vMyGQuz%c=->j&sB%mkOkN_MTfT-S)VNWd9b18zb%?FZca ztZf#^_xISlxl|)1te!9+wn2cuk^<AVz`DyXa{m!5TRqjPa!&y}24cIdyJT>hJ_LyT zuR$FQR(UC_!+n4j_bS;>ap^-`$I3HmU@=Tp&qY?<2^0X%M*{9S-U$<RtUBmrfCN&p zr8A%wUJ0<Bpe?Q{!9i@pVaov67&JocNa+MDIEnHVYT{j`J1YEBjo=`(SlDv>Ie-Vt zYN)K>`529^#5Is_;(cfEy6s(0;k{cdG)rMECQkqJMFJ!c3<6k-4aYwFR`kWcyY-I2 zB@JtE<xA^W0L?K(eTb_JW(1=a_1cHH+3PL@uesu3Og+yjR>n7|NeHSrp6dklGYD=k ztN_!ZI!)(Coe@9{EJ4T~n0g+SwZ73hM~$j8fc6$xUmW9|rL#g~xE%!6pa!l&4eaTd zr2J2+6dbUM)!N(Grx*d?BbASt9!g07LfZy&BlqF}pRZsM0CbLK*4Y6qR<b`J@2VZm ztkx}@Pt)Jvub{iIrW=b1s=kQI7ZBf%O#otRL9TCv-FNJ{l>Lj^1!urt#y^7S#A!<* zx=3Z?rgyz1fUTOi#n)0SKNt36Z`<K=5)r`KkN02D@a`qV#``Zwq!me;K|*+kA&m+S zx!n$v<_<%%UwZyk-&g=iDvynL2_Qiuu+<YIT*dAtO#=*&Q{GQc$@)fu@*%pfC^&7b zaB!^NpyYa^Zy*5Cl|Xd7C%VeFaLD6BFy0eg1*(1@iHd`tHlaTi_)8bN5WouawLX3N zR318XXm^*N_*IDnAh?r}v?t*kkL6Ux0D0qct&}LL<M|XF9i5Kk@g$0h1c!X_$&JS= zY$gAWE!{-e*YJ^BI)yVpo|<ryWG-^W!3;_k)$x2o60gFV^AJU+?3E4#w+wc|@$$*v zl}{i7g1eo9Q?B5UpEr=7p~^2MZFm^5H~qC>js+Ni1V2pyDo=pe!hMMcfoEu=tQkc+ z0i1Fkf#hzc=#(QmtOS2VPVjUVzBEYy$rYng#NT;%LM6lukc(#6HOK}3ZmLPOwFQL} zD&n5%`zX55u5^$C@sPN_TCTOOv8nz#xvl~E^IcrBkF8uk%$o_*B?Esr$vF?iCPtN1 zG&eVYBWFKsTOkCH@Fa-t8Mwn;%OEoV_m9CpM<Jy^H-bd~NgoM&pP~!iN(a1mW0(H& zVC9(oD2xCK;2f-RzoqEp7adqP!5YC@JUD<ay%InXI3c=b#LwMJ85w}_<6tbV=+Nzp zCk$FsF9tjJgYd+0MF>w(m#*5mJeXqvLR0q;-a9yPC3Y+S9U2eVtI`Pchxm9xMTV^% z#P%iFT9lS>`V(RTC<<|}l=t8UcQYko0H)uA=fxpTExT7L0Tf69YzO?K=mKPbmDgdg z{4H39aF<=Fl}5jO1R_u*<*>b&K+y%j0D^lM40ScC#8*Uo?-_wO2yP*aXEA#zIIw#e z$axt~&+1OUa}U4^Yv@%Vz^z;l89;0wf?KYBtA~;IiU0~H1)}=^3sZD{G61tZFguv> zxUE-Jr}KqSJm)}kytKy;!GY6Z*gTz=48sqU07NHFqBNl$Zs~r30me%{22L@7@;1!7 zaOYdv9}5sLCcQKu&SK7|_>R5u0iO-vlcbk|V4U(35GPcgKsVgtbzuOpeG1ExKLzYT zZv;+9VQr-7I#xPZ3H<;b9981464p0b=lDA>=W8PX)lGnH$5(FdzyK`j!6KXmLIQFT z2*=riDy-i3qXS)w4h$ZI;P^mxxhjqAwi7^|g)M@$=vZ{C5r0ZM5q=;6nF;jA8vc3o zp}Vadg!Tz6UuM2+o&=62fTBAMqSMt4QxNYy>{8D-ngqX*K#&MPbmICRJw3+f`XHgz zb2l3fUWmPQqoJrUe~O+t96xr5Pux63^N;jFoQq+adT#LXv&%V@NFWk|`yCw9?6Dn$ zwjRczB}%M5J^GZ8kZ>Xja)lN}hn2}r#Ks$z=&`Vs95ag21lyz~(Lrnn5fdMSqvXQV zvu`L)@m%CDwIDjg@gvSo&PFVJZm^z<TIsQiQ7I_agK8EX{BOb5?az20q;%f&j|9X? zl!a;*9Q<Dgv7O3Pl7K1*h%Ez(_Bl0*4u0=||29>CrjsP#BY>h!fo;Ke)%>{tV%ra& zr}|>xI|+o2z`j@K;l^;i9t(g%&V#Aukv=#U71wg{GMvy=9D5Fw0E+lZh)(f0!-S8^ zV1eJfBv4>no&*9RfFh2C&4Vdcbl9yFm$@ef5+|1>0mTr2*iL~d@`fq!OI+EWp%_?n zgao7@fWl6Ny@R$Cb3z6F?1o(;MSN_71QbUA#XTOj3yy_cbnrtA;kpjS<2J=r3LTP+ z018~(f%ZVCMTg_IrSM^(WP!0E5>P0CXbA33_!hFQ9QuDfE-r3}Lix}w5|EYv3O*hd zlE?U9%O)<p$&xlAwnhR9ClDJOI}F9Y6>bJ?O9!DHfL#^{nvtViIIG#~u>gT|&1FeI zIs#EqQDY&7pghtC{fOrVH%jL{TOtAV5eSC}oG|gkdRe!6=wk_tXD8KHBlK8G0*Q%< z;@IwXm~5j`up9d-+8->X-)xHnR7(J2I0^PN0+4(whjzDO*EL7AaOgM*$VA|DY>D1O z+meY6ViTiEE=QYTGWpDANkF{>AdYh(I!Q*Qpsi-KB~Fq=sh0>nCjprW;L@H-h@}bL z1hl1t*d9e2ePni*&69xo2|#d*5Jj&A2eFAuZ-%O$1S24U;1K8w(e>(HDG(d)RU&_m zp555=7RLg}&p9+bKzu3K)qEGd`@PkJ@Wg#friI31_DTXOCjjvjz{G{4eu)kOy;kKR z(|r;MDS-$Gt_-#hUi_lyFwY-hd;(5L)5XqJPXHo{fzAIV!QuCp@Vim<!D>8!urXz8 z?$daT+6fP;Ispf1&my9qZR+sbHcZdePPB}K1Qb93!WsqJ3|HDkhwpoF@OFg)+@lL5 zpgjT*n0Snj_qoV;KZLE`4cd#65s`p`2ta5RuqKFYIo=Z#<QiQe0ZkGR7u8iDGNUG= zV^HcKu;Rps7LEl_N3Q5436KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^qzLm;pJ^A~U|fIp--4+)R}36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@sDnW3Sb!7HE-K8Qq7IMgB?*uK36KB@kN^pg011!)36KB@kN^pg h011!)2?UqGvXAF2N_u_jy`tBmapxC4an6j!{{W3OzWD$E literal 0 HcmV?d00001 diff --git a/build.gradle b/build.gradle index ca3f72c..99a402a 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ repositories { javafx { version = "25.0.2" - modules = ['javafx.controls', 'javafx.fxml', 'javafx.base'] + modules = ['javafx.controls', 'javafx.fxml', 'javafx.base', 'javafx.graphics', 'javafx.web'] } dependencies { From e5e93c1a65ed62c17a149bc8fcdb028bcbb3c85e Mon Sep 17 00:00:00 2001 From: Jona Walpert <98217482+Jona-Walpert@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:20:18 +0100 Subject: [PATCH 27/31] Add: Added "bin" to gitignroe --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index aa33988..c29286e 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,7 @@ $RECYCLE.BIN/ *.drawio.pdf ## IntelliJ -.idea \ No newline at end of file +.idea + +## bin +bin/ \ No newline at end of file From 2b012aa256cf9e0ca3077726a7713b2c31eb4988 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 17:40:46 +0100 Subject: [PATCH 28/31] Add: log4j as gradle dependency --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 99a402a..0a481cf 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,10 @@ javafx { } dependencies { + // Source: https://mvnrepository.com/artifact/org.apache.logging.log4j + implementation("org.apache.logging.log4j:log4j-api:2.25.3") + runtimeOnly("org.apache.logging.log4j:log4j-core:2.25.3") + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.10.0") } From 85c872df239b39329c8ff76f43b6e29bec237df9 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 17:41:14 +0100 Subject: [PATCH 29/31] Add: Konfigure log4j with the bare minimum --- src/main/resources/log4j2.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/resources/log4j2.xml diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..1c7e406 --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="warn"> + + <Appenders> + <Console name="Console" target="SYSTEM_OUT"> + <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + </Console> + </Appenders> + + <Loggers> + <Root level="info"> + <AppenderRef ref="Console"/> + </Root> + </Loggers> + +</Configuration> \ No newline at end of file From a2256b2c1e3bd6747b75cfb85c5b84410d1e35d3 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 17:44:19 +0100 Subject: [PATCH 30/31] Add: Jansi as gradle dependency to allow colored console output --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 0a481cf..8196bdb 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,9 @@ dependencies { implementation("org.apache.logging.log4j:log4j-api:2.25.3") runtimeOnly("org.apache.logging.log4j:log4j-core:2.25.3") + // Source: https://mvnrepository.com/artifact/org.fusesource.jansi/jansi + runtimeOnly("org.fusesource.jansi:jansi:2.4.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.10.0") } From 18331a7cc3e5153ad459ce16c08f4c20f445487a Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer <lars.winzer@stud.unibas.ch> Date: Wed, 11 Mar 2026 17:47:36 +0100 Subject: [PATCH 31/31] Add: Colored log output, based on log level --- src/main/resources/log4j2.xml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 1c7e406..01d74ca 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -3,12 +3,21 @@ <Appenders> <Console name="Console" target="SYSTEM_OUT"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> + <PatternLayout disableAnsi="false" pattern= + "%style{%d{HH:mm:ss.SSS}}{bright,black} %highlight{%-8level}{ + FATAL=bright red, + ERROR=red, + WARN=yellow, + INFO=blue, + DEBUG=cyan, + TRACE=white + } %style{%-20logger{20}}{magenta} %msg%n" + /> </Console> </Appenders> <Loggers> - <Root level="info"> + <Root level="debug"> <AppenderRef ref="Console"/> </Root> </Loggers>