<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[MUnique OpenMU Project]]></title><description><![CDATA[Thoughts, stories and ideas during implementation of a MMORPG Game Server]]></description><link>https://munique.net/</link><image><url>https://munique.net/favicon.png</url><title>MUnique OpenMU Project</title><link>https://munique.net/</link></image><generator>Ghost 5.82</generator><lastBuildDate>Sun, 26 Apr 2026 12:02:19 GMT</lastBuildDate><atom:link href="https://munique.net/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Optimized pathfinding]]></title><description><![CDATA[<p>After some tests of the community, we found that many instances of monsters on a map will cause a lot of memory usage. As I found out, the root of the cause was how we do the pathfinding for the monsters. Because of that, I took this as a challenge</p>]]></description><link>https://munique.net/optimizing-pathfinding/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc8</guid><category><![CDATA[C#]]></category><category><![CDATA[OpenMU]]></category><category><![CDATA[MU Online]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Sun, 18 Dec 2022 12:04:59 GMT</pubDate><media:content url="https://munique.net/content/images/2022/12/_texture-g5aa66f0f7_1920.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2022/12/_texture-g5aa66f0f7_1920.jpg" alt="Optimized pathfinding"><p>After some tests of the community, we found that many instances of monsters on a map will cause a lot of memory usage. As I found out, the root of the cause was how we do the pathfinding for the monsters. Because of that, I took this as a challenge to optimize memory and cpu usage.</p><h2 id="reducing-memory-usage-1">Reducing memory usage (1)</h2><p>The major flaw was using one instance of a pathfinder per monster. Every instance of a pathfinder had one instance of a &quot;<em>GridNetwork</em>&quot;, which had an array of 65536 possible nodes. These nodes were created by demand, but kept in memory to be reused, until the server closed.</p><p>So, the first thing I did was pooling these pathfinder objects per map. A handful of them could easily handle all monsters of a map, because a monster would only search for a path every few seconds, and only if a player is around. This has probably already solved the intial issue, but I wanted more.</p><h2 id="reducing-cpu-usage">Reducing CPU usage</h2><p>I observed that the preparing of the path finder between each request was pretty inefficient. It still had to reset all the nodes - up to 65k as mentioned above. Additionally, searching for a path in some maps could take longer than expected. Take the <em>Lost Tower</em> map as example. There you have a lot of walls, where the monster would see a player in their range, but could never find a path in a reasonable number of steps. For example, the limit for the walking network packet is 16 steps. The line of sight distance limit of a walk target (MonsterDefinition.ViewRange), is much lower than that (at most: 10).</p><p>So, I had the idea of limiting the search of the path to a small segment of the map. To accomplish that, I implemented a &quot;<em>ScopedGridNetwork</em>&quot;, which would use a dynamically sized segment, of 8x8 or 16x16 coordinates. To define the segment, we calculate the point in the middle of the start and end coordinates and then check if both coordinates fit into a 8x8 or 16x16 segment.</p><p>The smaller segment means less work to do: First, we don&apos;t have to prepare up to 65k nodes anymore. Second, we are not dedicating a lot of computation to hopeless requests anymore - we fail much earlier.</p><h2 id="reducing-memory-usage-2">Reducing memory usage (2)</h2><p>So, because we&apos;re now using map segments, we can reduce the size of the node array in the network to at most 256 (16x16) elements, instead of 65k.</p><p>Now, because the CPU usage was greatly reduced, why not handle all monsters of all maps with a handful of pooled Pathfinding objects? Well, I implemented this as well, of course :-)</p><h2 id="summary">Summary</h2><p>Thanks to this <a href="https://github.com/MUnique/OpenMU/pull/276?ref=munique.net">changes</a>, we have now a very efficient pathfinding implementation in OpenMU. To give you an idea: One pathfinding request takes an average of 0.025 ms on my machine (Ryzen 9 5900X). That means, it can handle about 40k pathfinding requests per second, per cpu core.</p><p></p>]]></content:encoded></item><item><title><![CDATA[The power of the Attribute System]]></title><description><![CDATA[I want to show a nice powerful feature of the OpenMU Project - the Attribute System.]]></description><link>https://munique.net/power-of-the-attribute-system/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc7</guid><category><![CDATA[OpenMU]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Mon, 27 Apr 2020 19:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2020/04/table-music-power-sound-63703--1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2020/04/table-music-power-sound-63703--1-.jpg" alt="The power of the Attribute System"><p>I want to show a nice powerful feature of the OpenMU Project - the Attribute System.</p><p>First, I want to describe some terms:</p><ul><li><strong>Attribute Definition</strong>: Describes the attribute itself. For example, we have definitions for each possible attribute, such as <em>Strength</em>, <em>Agility</em>, <em>Character Level</em>.</li><li><strong>Stat Attribute</strong>: It&apos;s an attribute which is saved individually for a character. A stat attribute can be increasable by the player (e.g. <em>Strength</em>, <em>Agility</em>, etc.) or just increasable by the program code (e.g. <em>Level</em>, <em>Current Health</em>, etc.).</li><li><strong>Attribute (Value)</strong>: The value of an attribute of a game object, e.g. Character A has &apos;50 <em>Strength</em>&apos;. Values are all handled as 32 bit floats.</li><li><strong>Attribute Relationship</strong>: It&apos;s possible to define relationships between attributes. For example, &quot;1 <em>Strength </em>-&gt; +0.25 <em>Maximum Damage</em>&quot;.</li><li><strong>Attribute System</strong>: An object which holds and manages all attributes of a game object (e.g. <em>Player</em>, <em>Monster</em>). Every game object which attacks or can be attacked has an Attribute System object.</li><li><strong>Item Option</strong>: An item option holds a so-called &quot;PowerUpDefinition&quot; which defines which target attribute is increased by which value. The value can consist of a constant value and/or attribute relationships. For example, you could define an option which increases the base damage by the character level.</li><li><strong>(Passive) Skills</strong>: The master skill system of MU Online has some skills which give some power-ups when they&apos;re learned. These power-ups are also described with a &quot;PowerUpDefinition&quot;, just like item options.</li><li><strong>Attribute Requirements</strong>: It&apos;s possible to define attribute requirements for Items, Skills and Maps. E.g. &apos;Sword X needs 100 <em>Strength </em>to be equipped&apos; or &apos;Skill Y uses 20 <em>Mana&apos; </em>or &apos;Map Icarus requires the attribute &quot;CanFly&quot; greater than 0&apos; (which is given by Wings or a Dinorant).</li></ul><p>The attribute definitions, relationships, base values, requirements and item options are all defined as data in the database. So, as you can see, the system as a whole allows you to tune every tiny bit of your in-game formulas and to customize a lot without changing a line of code.</p><h2 id="how-to-use">How to use</h2><h3 id="attribute-relationships">Attribute Relationships</h3><p>One of the most important things are the attribute relationships. You can find these in the <em>CharacterClass </em>configurations of each character class. In the example above, 1 <em>Strength </em>is multiplied with 0.25 to get the additional value for the maximum damage.</p><p>Instead of multiplying, there are other operators available, too. You can do simple additions or even exponentiate the source attribute with a value. There is one limitation for attribute relationships, though: Stat Attributes can not be defined as targets. For <em>Strength</em>, etc. there are two attributes: <em>BaseStrength </em>(stat attribute) and <em>TotalStrength </em>(can be a target); There is actually a relationship which adds the <em>BaseStrength </em>to the <em>TotalStrength</em>.</p><p><strong>Example &apos;Dynamical experience rate&apos;</strong></p><p>By dynamical, I mean calculating the rate based on some other attribute, such as the character level. Usually, game servers are configured with a fixed experience rate, for example 1x or 1000x which is applied in the same way to every character.</p><p>Since the &apos;experience rate&apos; is an attribute of a character, it&apos;s possible to adjust it by defining additional relationships. The default attribute value here is 1. However, what about defining something like &apos;Add the square root of the character level to the experience rate&apos;. For example, a level 9 character would be given an additional rate of 3, whereas an level 225 will get an additional rate of 15.</p><p>With the available <em>exponentiate </em>operator we can also calculate square roots, when we choose 1/2 as the operand.</p><p>To actually configure this, we need to navigate to the character class configuration in the Admin Panel:</p><ul><li>First, navigate to the Game Configuration</li><li>Then, scroll a bit down to the Character Classes, expand them and click the edit button for one of them, e.g. Dark Knight:</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://munique.net/content/images/2020/04/character_classes.PNG" class="kg-image" alt="The power of the Attribute System" loading="lazy"><figcaption><span style="white-space: pre-wrap;">Game Configuration</span></figcaption></figure><ul><li>At the character class page, you&apos;ll find the list of attribute relationships (called AttributeCombinations) a &apos;Create&apos; button when expanded. When you have opened the creation dialog, fill in the following:</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://munique.net/content/images/2020/04/exp_by_squareroot_of_level.PNG" class="kg-image" alt="The power of the Attribute System" loading="lazy"><figcaption><span style="white-space: pre-wrap;">Create a new attribute relationship</span></figcaption></figure><p>Hit Submit, Save and it&apos;s done. Well, a server restart is still required - I&apos;m working on that ;)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://munique.net/content/images/2020/04/exp_by_squareroot_of_level_result.PNG" class="kg-image" alt="The power of the Attribute System" loading="lazy"><figcaption><span style="white-space: pre-wrap;">The result</span></figcaption></figure><p><strong>More use-case ideas</strong></p><p>So, if you not already identified some more practical use-cases I want to give you some ideas:</p><ul><li>Modify or add new item options</li><li>Giving new attributes to Items, Monsters, Character classes</li><li>Defining attribute requirements for Items, Maps</li><li>Modifying the damage calculations by changing the relationships in the character classes</li><li>Adding new attribute definitions and relationships to the game which you may use further to restrict Map access or give some new powers.</li></ul>]]></content:encoded></item><item><title><![CDATA[Project Status - Easter 2020]]></title><description><![CDATA[I want to give a short status update on the OpenMU project.]]></description><link>https://munique.net/project-status-easter-2020/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc6</guid><category><![CDATA[OpenMU]]></category><category><![CDATA[MU Online]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Sun, 12 Apr 2020 21:01:57 GMT</pubDate><media:content url="https://munique.net/content/images/2020/04/cute-cuddly-toy-cartoon-costume-4142--1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2020/04/cute-cuddly-toy-cartoon-costume-4142--1-.jpg" alt="Project Status - Easter 2020"><p>I want to give a short status update on the OpenMU project.</p><h2 id="recap-of-2019">Recap of 2019</h2><p>As you may <a href="https://munique.net/recap-of-2019/">remember</a>, I set some goals for 2020.</p><h3 id="stabilizing-and-getting-the-network-nuget-out">Stabilizing and getting the Network-NuGet out</h3><p>I put some effort in solving some edge cases in the network code - primarily related to error handling and detecting disconnections. There have been some usages of obsolete System.IO.Pipeline functions, which I resolved.</p><p>Additionally, I improved the API of the network &amp; packets API. There are now simple extension methods for IConnection to send specific packets. They&apos;re automatically generated by the packet definition XML files, too.</p><p>Last but not least, I released nuget packages on nuget.org, so that other applications can make use of this API.</p><h3 id="release-of-an-updated-network-analyzer">Release of an updated Network Analyzer</h3><p>TODO</p><h3 id="completion-of-the-quest-system">Completion of the Quest System</h3><p>I completed the first part of it. The old quest system with the level 150, 220, 380 and 400 quests was implemented and tested. I hope that I can implement the other quest system without much changes to the logic, but only by adding its data. Time will tell.</p><h3 id="getting-other-game-features-done">Getting other game features done</h3><p>I started solving some gameplay issues and missing features:</p><ul><li><a href="https://github.com/MUnique/OpenMU/issues/4?ref=munique.net">Implement usage of ammunition (Arrows, Bolts) for Bows/Crossbows</a></li><li><a href="https://github.com/MUnique/OpenMU/issues/149?ref=munique.net">Automatic Health Regeneration in the Safezone</a></li><li><a href="https://github.com/MUnique/OpenMU/issues/160?ref=munique.net">Weather System</a></li><li><a href="https://github.com/MUnique/OpenMU/issues/150?ref=munique.net">Drop Level for Event Items</a></li><li><a href="https://github.com/MUnique/OpenMU/pull/173?ref=munique.net">Ancient Sets</a></li><li>Chaos Machine Combinations (in progress, about 30% done)</li><li>Fruit consumption</li></ul><p>From the community, I received some nice contributions of chat commands. Thanks for that!</p><h3 id="simplifying-modularization-of-the-server-public-api">Simplifying/Modularization of the server -&gt; Public API</h3><p>I implemented a basic public API. However, further modularizing the whole server architecture will take some more time and has not a high priority at the moment.</p><p>I think of implementing the &quot;servers&quot; as <a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostedservice?view=dotnet-plat-ext-3.1&amp;ref=munique.net">IHostedService</a> which would run on a <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.0&amp;ref=munique.net">Generic Host</a>. It would give us some nice built-in goodies, such as logging and dependency injection.</p><h2 id="other-improvements">Other improvements</h2><h3 id="blazor">Blazor</h3><p>One big change was the technological switch of the AdminPanel from React/Redux to server-side Blazor. The reasons for the change were:</p><ul><li>Keeping most of the code in C#</li><li>Less and simpler code. React/Redux involves alot of boilerplate code which is sometimes hard to follow.</li><li>Being more productive. If I compare how much time I spent on react/redux and on blazor, I can say that I got more features done in blazor in less time.</li></ul><p>One big improvement is now the ability to edit the server configuration and account data on the admin panel. Not only small parts of it, but every tiny bit. The user interface is dynamically generated by reflection. Not the fastest way, but you would be surprised how fast it is anyway. If performance of that is ever a problem, there are still ways to improve that.</p><p>There is still a lot of things to do in the AdminPanel:</p><ul><li>General polishing, adding user-friendly field names and descriptions</li><li>Adding some kind of editors, also graphical ones (e.g. for gates of maps)</li><li>Adding authentication</li><li>Better error handling on generic edit pages which tells the user what went wrong.</li></ul><h3 id="documentation-generation">Documentation generation</h3><p>I implemented the automatic generation of packet documentation with a XSL transformation which takes the packet definition XMLs and transforms them to markdown files.</p><p>Additionally to that, I set up GitHub Pages with Jekyll, which takes these all documentation of the git repo folder <em>docs </em>and spits out html files. You can see a link to the documentation at the menu of this page.</p>]]></content:encoded></item><item><title><![CDATA[Demo Console Client for MU Online]]></title><description><![CDATA[<p>For a test of the new nuget packages of <em>MUnique.OpenMU.Network.Packets</em> I wrote a small demo client. </p><p>You can find the source code here: <a href="https://github.com/sven-n/MuConsoleTestClient?ref=munique.net">https://github.com/sven-n/MuConsoleTestClient</a></p><p>It encapsulates most of the packet creation and parsing for you, so you don&apos;t have to mess</p>]]></description><link>https://munique.net/demo-console-client/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc5</guid><category><![CDATA[C#]]></category><category><![CDATA[OpenMU]]></category><category><![CDATA[MU Online]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Thu, 12 Mar 2020 21:33:58 GMT</pubDate><media:content url="https://munique.net/content/images/2020/03/abstract-art-blur-bright-373543.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2020/03/abstract-art-blur-bright-373543.jpg" alt="Demo Console Client for MU Online"><p>For a test of the new nuget packages of <em>MUnique.OpenMU.Network.Packets</em> I wrote a small demo client. </p><p>You can find the source code here: <a href="https://github.com/sven-n/MuConsoleTestClient?ref=munique.net">https://github.com/sven-n/MuConsoleTestClient</a></p><p>It encapsulates most of the packet creation and parsing for you, so you don&apos;t have to mess around with bits and bytes yourself. Recently, I also added some automatically generated extension methods to make sending packets easier than ever.</p><p>Have fun :)</p>]]></content:encoded></item><item><title><![CDATA[Recap of 2019]]></title><description><![CDATA[In this post I want to recap what was accomplished in 2019 and what will come next in 2020.]]></description><link>https://munique.net/recap-of-2019/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc4</guid><category><![CDATA[OpenMU]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Thu, 09 Jan 2020 00:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2020/01/dark-1845065_960_720.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2020/01/dark-1845065_960_720.jpg" alt="Recap of 2019"><p>Hey folks, in this post I want to recap what was accomplished in 2019 and what will come next in 2020.</p><h2 id="all-the-way-to-net-core">All the way to .net core</h2><p>We managed to migrate the whole project to .NET Core 3 and also 3.1. This enabled some new opportunities, simplifications and optimizations in the code base. &#xA0; &#xA0; </p><h2 id="docker">Docker</h2><p>With the help of some people of the community we managed to provide a Dockerfile and a publish <a href="https://hub.docker.com/r/munique/openmu?ref=munique.net">images</a> on DockerHub. Additionally, there is a docker-compose configuration to deploy a server including the postgres database with one simple command. You can find the required information in the <a href="https://github.com/MUnique/OpenMU/blob/master/QuickStart.md?ref=munique.net">QuickStart-Guide</a>.</p><h2 id="packet-structures">Packet Structures</h2><p>In the previous posts I talked about how ref structs can be used to parse and write data into a <em>Span&lt;byte&gt;</em> and how these structs can be generated from some data on compile time. I managed to set up a XSL transformation to make that happen, changed almost all of the affected code to make use of these structs.</p><p>The first tool which benefits the most of this yet is the NetworkAnalyzer. There is no new release with it yet, however if you can compile it yourself, you&#x2018;ll be surprised how well it extracts all the available data out of the network data.</p><p>I plan to release a NuGet-Package of the Network-Assembly to make it easier to develop other network based tools for the game, too.</p><h2 id="plugin-system">Plugin System</h2><p>Another great addition this year was the introduction of a plugin system. It allows us to select the right plugins for each connected client separately, depending on the game client version. For the outgoing messages, I basically broke up all the &#x201E;Views&#x201C; &#xA0;(interfaces with a lot of methods) into smaller &quot;ViewPlugIns&quot; which mostly only have one method and send one message. On the incoming side of data, I made a &quot;PacketHandlerPlugIn&quot; out of every existing &quot;PacketHandler&quot;.</p><p>This enabled new possibilities to support other game clients than only season 6. However, the main focus is still season 6. Anyone is free to add their own plugins to support newer or older clients, though. It&#x2019;s even possible to connect to the same game server and join the same game world using two different game client versions. I managed to do that with Season 6 and one of the oldest MU Online versions which you &#xA0;can find - 0.75. At the moment, I have no more interest in extending the 0.75 stuff, because this client is buggy as hell - nobody would ever want to play it in this state. However, in the future I also want to add support for 0.97d where more stable clients are available.</p><p>Of course, there are other use cases for plugins as well. In the code you can find several plugin extension points where custom code can be plugged into.</p><h2 id="ancient-simplemodulus">Ancient SimpleModulus</h2><p>In order to make the server compatible with a game client of version 0.75, I also had to make the network encryption work. It turned out that this version used the very first variant of the SimpleModulus algorithm. After banging my head on the keyboard a few times I managed to find out that this variant uses a much bigger block size than the newer variant. With some luck, I managed to decrypt the client side encryption keys and calculated the server side ones after I adapted the key generator.</p><p>This all also helped me to better understand the algorithm, so I implemented some minor improvements to support longer keys with the same code base, too.</p><h2 id="quest-system-in-progress">Quest System - In progress</h2><p>To get some game features done, I started to begin implementing the quest system. It&#x2019;s currently still incomplete. The first thing which was done is defining all the required network message structures and implementing the corresponding View- and PacketHandler-Plugins. Then I went further and tried to make some sense out of packets I captured a decade ago on the original Global MU Online server and implemented some of the game logic.</p><p>Now, what&#x2019;s missing is the initialization data for all the available quests and a lot of testing ;)</p><h2 id="outlook-for-2020-and-beyond">Outlook for 2020 and beyond</h2><p><em>Disclaimer: I have a lot of ideas for the future, but unfortunately I lack of time. This will probably even get worse because I got a new job starting with this year which will need my full focus. I still hope to get something nice done this year.</em></p><p>My goals for this year are:</p><ul><li>Stabilizing and getting the Network-NuGet out</li><li>Release of an updated Network Analyzer</li><li>Completion of the Quest System</li><li>Getting other game features done which are lurking around in the GitHub issues</li><li>Simplifying/Modularization of the server -&gt; Public API</li></ul>]]></content:encoded></item><item><title><![CDATA[Generating message structs by data]]></title><description><![CDATA[In this post I want to explain how I'm planning to generate ref structs in OpenMU and how this fits into the big picture.]]></description><link>https://munique.net/generating-message-structs-by-data/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc3</guid><category><![CDATA[C#]]></category><category><![CDATA[OpenMU]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Tue, 20 Aug 2019 02:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2019/08/markus-spiske-gcgves5H_Ac-unsplash_low-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2019/08/markus-spiske-gcgves5H_Ac-unsplash_low-1.jpg" alt="Generating message structs by data"><p>In my <a href="https://munique.net/handling-packet-structures-dotnet/">previous post</a> I have presented my new idea of handling network packets using <em>ref structs</em>. In this post I want to explain how I&apos;m planning to use them in OpenMU and how this fits into the big picture.</p><h2 id="packet-structures-as-data">Packet structures as data</h2><p>The first thing to do is building up something like a database of message struct definitions. I want to save the following stuff for each packet:</p><!--kg-card-begin: markdown--><ul>
<li>Struct Name</li>
<li>Descriptions (&apos;sent when?&apos;, &apos;caused reactions?&apos;)</li>
<li>Header type (C1 etc.)</li>
<li>Code and SubCode, if applicable</li>
<li>Direction</li>
<li>Expected length, if known</li>
<li>Fields, for each field:
<ul>
<li>Index of the field in the struct</li>
<li>Size</li>
<li>Type (Integer, String, Enum etc.)</li>
<li>Byte order (endianness)</li>
<li>Name</li>
<li>Description</li>
</ul>
</li>
<li>Enum Types, for each with their possible values, including names and descriptions. If an enum is used by more than one packet, an external definition should be possible.</li>
</ul>
<!--kg-card-end: markdown--><p>As you see, I want to define more than just what&apos;s required to generate some struct code. I also want to be able to generate a documentation of all message structs. This also means, whenever a message gets extended or needs to be handled by the server, the corresponding data must be extended beforehand.</p><p>A similar, but incomplete collection of this data is already contained within the Network Analyzer project in some XML files. These files could be replaced with the new approach, once it&apos;s complete. How the new data is stored, isn&apos;t clear yet. I have some ideas, but it&apos;s too early to talk about it.</p><h2 id="generating-and-compiling">Generating and Compiling</h2><p>The next step is generating the code out of the available information. The generated code could look like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">/// &lt;summary&gt;
/// Is sent when:
///   The client opened an quest NPC dialog and decided to start an available quests.
/// Causes the following actions on the server side: 
///   The server decides if the character can start the quest. A character can run
///   up to 3 concurrent quests at a time.
/// &lt;/summary&gt;
[SentWhen(&quot;The client opened an quest NPC dialog and decided to start an available quests.&quot;)]
[ReactionsOnServer(&quot;The server decides if the character can start the quest. A character can run up to 3 concurrent quests at a time.&quot;)]
[Direction(PacketDirection.ToServer)]
[Length(9)]
public ref struct QuestInitializationRequest
{
    public static byte Type =&gt; 0xC1;

    public static byte Length =&gt; 9;
    
    public static byte Code =&gt; 0xF6;
    
    public static byte SubCode =&gt; 0x0A;
    
    private static readonly byte QuestNumberIndex = 4;
    
    private static readonly byte QuestGroupIndex = 6;
    
    private static readonly byte UnknownFieldIndex = 8;

    private Span&lt;byte&gt; data;

    private QuestInitializationRequest(Span&lt;byte&gt; data)
    {
        if (data.Length &lt; Length)
        {
            throw new ArgumentException($&quot;Expected a span which is at least {Length} bytes long&quot;);
        }
        
        this.data = data;
        
        var header = this.Header;
        if (header.Type != Type)
        {
            throw new ArgumentException($&quot;Wrong header type. Expected: {Type}, Actual: {header.Type});
        }
        if (header.Code != Code)
        {
            throw new ArgumentException($&quot;Wrong header code. Expected: {Code}, Actual: {header.Code});
        }
        
        if (header.SubCode != SubCode)
        {
            throw new ArgumentException($&quot;Wrong header sub code. Expected: {SubCode}, Actual: {header.SubCode});
        }
    }
    
    /// &lt;summary&gt;
    /// Gets or sets the header of this message.
    /// &lt;/summary&gt;
    public C1HeaderWithSubCode Header
    {
        get =&gt; new C1HeaderWithSubCode(this.data); // this could be another ref struct
    }

    /// &lt;summary&gt;
    /// Gets or sets the number of the quest which should be initialized.
    /// &lt;/summary&gt;
    [FieldDescription(&quot;The number of the quest which should be initialized.&quot;)
    [BigEndian]
    public ushort QuestNumber
    {
        get =&gt; this.data.GetWordBigEndian(QuestNumberIndex);
        set =&gt; this.data.SetWordBigEndian(QuestNumberIndex, value);
    }

    /// &lt;summary&gt;
    /// Gets or sets the group of the quest which should be initialized.
    /// &lt;/summary&gt;
    [FieldDescription(&quot;The group of the quest which should be initialized.&quot;)
    [BigEndian]
    public ushort QuestGroup
    {
        get =&gt; this.data.GetWordBigEndian(QuestGroupIndex);
        set =&gt; this.data.SetWordBigEndian(QuestGroupIndex, value);
    }

    /// &lt;summary&gt;
    /// Gets or sets an unknown field.
    /// &lt;/summary&gt;
    [FieldDescription(&quot;An unknown field&quot;)
    public byte UnknownField
    {
        get =&gt; this.data[UnknownFieldIndex];
        set =&gt; this.data[UnknownFieldIndex] = value;
    }

    /// &lt;summary&gt;
    /// Performs an implicit conversion from a Span of bytes to a &lt;see cref=&quot;QuestInitializationRequest&quot; /&gt;.
    /// &lt;/summary&gt;
    /// &lt;param name=&quot;packet&quot;&gt;The packet.&lt;/param&gt;
    /// &lt;returns&gt;
    /// The result of the conversion.
    /// &lt;/returns&gt;
    public static implicit operator QuestInitializationRequest(Span&lt;byte&gt; packet) =&gt; new QuestInitializationRequest(packet);

    /// &lt;summary&gt;
    /// Performs an implicit conversion from &lt;see cref=&quot;QuestInitializationRequest&quot; /&gt; to a span of bytes.
    /// &lt;/summary&gt;
    /// &lt;param name=&quot;packet&quot;&gt;The packet.&lt;/param&gt;
    /// &lt;returns&gt;
    /// The result of the conversion.
    /// &lt;/returns&gt;
    public static implicit operator Span&lt;byte&gt;(QuestInitializationRequest packet) =&gt; packet.data;
    
    public static ThreadSafeWriter StartSafeWriting(IConnection connection, out QuestInitializationRequest message)
    {
        var writer = new ThreadSafeWriter(connection, Type, Length);
        var span = writer.Span;
        span[2] = Code
        span[3] = SubCode;
        message = span;
        return writer;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Once this is compiled, we could push this to a NuGet repository. This could also be done automatically by the continuous integration build.</p><h2 id="workflow">Workflow</h2><p>So, when adding a new field or message we first have to get an updated NuGet package. The workflow would be as follows:</p><ol><li>Extend the data, push to Git</li><li>CI will build and push it to the NuGet package repository</li><li>Wait some minutes...</li><li>Update the NuGet package reference in the consuming project (e.g. GameServer, Network.Analyzer)</li><li>You&apos;re ready to use the new message structures</li></ol><h2 id="usage">Usage</h2><p>So, now we have a NuGet package which can be referenced by the GameServer and used in all of the packet handler and view plugins. I want to give a short example about how to use them.</p><h3 id="reading-messages">Reading messages</h3><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class QuestInitializationRequestHandler
{
    public void HandlePacket(RemotePlayer player, Span&lt;byte&gt; packet)
    {
        QuestInitializationRequest request = packet;

        // now you can work with the fields of the request:
        Console.WriteLine(request.QuestNumber);
    }
}
</code></pre>
<!--kg-card-end: markdown--><h3 id="writing-messages">Writing messages</h3><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class SomeClass
{
    public void SendRequest(IConnection connection)
    {
        using (var writer = QuestInitializationRequest.StartSafeWriting(connection, out var message))
        {
            message.QuestNumber = 1234;
            message.QuestGroup = 42;
            writer.Commit();
        }
    }
}
</code></pre>
<!--kg-card-end: markdown--><h2 id="why">Why?</h2><p>You might ask, why should I do that? Well, I see the following benefits:</p><ul><li>A precise and consistent documentation is enforced, can be automatically generated and is even available in code.</li><li>Less errors when parsing and serializing messages, assuming the data is correct</li><li>We get a reusable message library (e.g. network analyzer, client simulator)</li><li>Writing code which writes code is always fun ;-)</li></ul><p>The downsides are obviously:</p><ul><li>Additional work. However, if we want a complete documentation about all message types, this kind of work is required anyway.</li><li>Additional build step. Because we&apos;ll reference a NuGet package for the messages, whenever we extend the messages we have to wait for the build to complete and for the published NuGet package update.</li></ul><p>All in all, I think it would be worth the effort.</p><h2 id="versioning">Versioning</h2><p>As you might know, the OpenMU game server supports network protocols of multiple game client versions since a few weeks. The idea would be to create one struct definition per variant, like we also use different packet handler and view plugins in the game server.</p>]]></content:encoded></item><item><title><![CDATA[Handling packet structures in .NET - a new way?]]></title><description><![CDATA[Parsing and serializing network packets in .NET in a safe, performant and elegant way can be quite tricky. C# ref structs might be the answer.]]></description><link>https://munique.net/handling-packet-structures-dotnet/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc2</guid><category><![CDATA[C#]]></category><category><![CDATA[OpenMU]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Tue, 20 Aug 2019 00:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2019/08/markus-spiske-gcgves5H_Ac-unsplash_low.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://munique.net/content/images/2019/08/markus-spiske-gcgves5H_Ac-unsplash_low.jpg" alt="Handling packet structures in .NET - a new way?"><p>As you may know, parsing and serializing network packets in .NET in a safe, performant and elegant way can be quite tricky. There are several ways of doing it, but none really satisfied me in all of these aspects.</p><p>Recently, I stumbled across <em><a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref?view=netstandard-2.1&amp;ref=munique.net#ref-struct-types">ref structs</a></em> which got introduced with C# 7.2. As you might know, OpenMU already handles incoming data packets as <em><a href="https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netstandard-2.1&amp;ref=munique.net">Span&lt;byte&gt;</a></em>, because it never allocates data on the heap for each packet and therefore reduces stress at the garbage collector. It&apos;s basically a reference to a piece of memory, e.g. a part of a buffer array which might be part of an array pool. A <em>Span&lt;byte&gt;</em> is a <em>ref struct</em> as well. I don&apos;t want to go into details here, but these <em>ref structs</em> have some constraints, such as you can&apos;t hold them in a field of a class or a regular struct. However, you can hold it in a field of another <em>ref struct</em> - that&apos;s what I&apos;m trying to do.</p><p>This allows to define <em>ref structs</em> like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">    public ref struct SomeMessage
    {
        private Span&lt;byte&gt; data;

        private SomeMessage(Span&lt;byte&gt; data)
        {
            // You could add length/header type checks here :)
            this.data = data;
        }

        public byte SomeField
        {
            get =&gt; this.data[2];
            set =&gt; this.data[2] = value;
        }

        public static implicit operator SomeMessage(Span&lt;byte&gt; packet)
        {
            return new SomeMessage(packet);
        }
    }
</code></pre>
<!--kg-card-end: markdown--><p>I think you know what I have in mind, don&apos;t you? ;-) Basically, this message struct encapsulates the underlying Span&lt;byte&gt; and offers a simpler access to fields. Additionally, you can now implicitly cast a Span&lt;byte&gt; into a message structure like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">void HandlePacket(Span&lt;byte&gt; data)
{
    SomeMessage message = data;
    Console.WriteLine(message.SomeField);
}
</code></pre>
<!--kg-card-end: markdown--><p>So, the packet handling code doesn&apos;t have to mess around with indexes anymore and isn&apos;t allocating memory on the heap. Of course, now the struct has to know all the field indexes, how these fields are aligned and how bytes are ordered. To solve this, I have another idea which I&apos;ll describe in another blog post.</p>]]></content:encoded></item><item><title><![CDATA[SimpleModulus revisited]]></title><description><![CDATA[About SimpleModulus in very early versions of MU Online.
Bigger block size, more keys and a missing counter.]]></description><link>https://munique.net/simplemodulus-revisited/</link><guid isPermaLink="false">6633f2d02fa90259941c3fc1</guid><category><![CDATA[MU Online]]></category><category><![CDATA[OpenMU]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Wed, 12 Jun 2019 01:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Recently, I was working on getting game client version 0.75 to work with OpenMU. One big obstacle was the SimpleModulus encryption which was somehow different in the earlier versions of MU Online. You can find an analysis of it <a href="https://munique.net/a-closer-look-at-the-mu-online-packet-encryption">here</a>. According to a change log in a leaked 0.65 server source, SimpleModulus was introduced with Version 0.74. Unfortunately, the server source (0.65) didn&apos;t include the source code of SimpleModulus - just a header and a lib file. As I found out later, it wasn&apos;t correct for this client anyway.</p><p>My first attempts decrypting a captured packet using the newer SimpleModulus variant failed miserably. The provided dat-files (they contain the encryption keys) of the game client were too big to make sense for me, too. After a while, I realized they contain more keys than newer variants. The key files were also encrypted (simple XOR) and of course, the encryption key for the files was longer in earlier versions. To find the longer key, I searched in the old main.exe for the first values of the known key and found the others. I had luck that Webzen just shortened the key.</p><p>After a while I managed to decrypt the captured packet and refactored my code so that it&apos;s possible to support both variants.</p><p>To make a long story short, I summarize the difference to newer versions:</p><ul><li>The block size. In 0.75 it used a block size of 32 decrypted and 38 encrypted bytes, instead of 8 decrypted and 11 encrypted. One 16 bit value needs 18 bits in encrypted state (because the multiplication result is bigger), so you can basically calculate the encrypted block size as follows:</li></ul><p>&#x2003;&#x2003;&#x2003;blockSize<sub>encrypted</sub> = (blockSize<sub>decrypted</sub> * 18 / 16) + 2</p><ul><li>More keys. Because blocks were bigger in earlier versions, they used more keys. Every 16 bit value is encrypted separately, so it uses 16 keys ( 32 decrypted bytes / 2 bytes).</li><li>The missing counter. The old variant didn&apos;t use a counter in its header. The first byte in the first encrypted block was already the content. Webzen probably has added the counter in a later version to make replay attacks harder.</li></ul><p></p><p>It&apos;s pretty interesting that Webzen actually decreased security in one aspect (key size/count) and increased it by a counter. I can just assume that they wanted to reduce the required network traffic and CPU utilization back at the time (~2002). Needless to say, this algorithm isn&apos;t secure, no matter which variant is used.</p>]]></content:encoded></item><item><title><![CDATA[Understanding the relationship between react, redux and react-redux]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em>react</em> and <em>redux</em> are basically independent libraries. You can use react without redux and redux without react. What glues both together is the <em>react-redux</em> library. I don&apos;t want to write a full blown tutorial about react and redux, so I&apos;ll keep it short.</p>
<blockquote>
<p>Disclaimer: I&apos;</p></blockquote>]]></description><link>https://munique.net/react-redux-relationship/</link><guid isPermaLink="false">6633f2d02fa90259941c3fbe</guid><category><![CDATA[Redux]]></category><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Thu, 14 Jun 2018 01:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2018/09/logo-title-dark.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://munique.net/content/images/2018/09/logo-title-dark.png" alt="Understanding the relationship between react, redux and react-redux"><p><em>react</em> and <em>redux</em> are basically independent libraries. You can use react without redux and redux without react. What glues both together is the <em>react-redux</em> library. I don&apos;t want to write a full blown tutorial about react and redux, so I&apos;ll keep it short.</p>
<blockquote>
<p>Disclaimer: I&apos;m not an expert. My explanations could be na&#xEF;ve ;)</p>
</blockquote>
<h5 id="react">react</h5>
<p><em>react</em> is obviously the library which renders the components to the DOM.</p>
<h5 id="redux">redux</h5>
<p><em>redux</em> offers mechanisms to handle the state of your application.</p>
<h5 id="reactredux">react-redux</h5>
<p><em>react-redux</em> is used to &quot;connect&quot; your React components to the store.<br>
It has the &apos;Provider&apos; component which makes your store available in its contained components (your app). It offers the &quot;connect&quot; method which has knowledge of this provided store. In your components you basically define all of your application state and actions in your props and don&apos;t access the store directly.</p>
<h6 id="presentationalandcontainercomponents">presentational and container components</h6>
<p>In React it&apos;s usually a good pattern to separate your components into <a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0?ref=munique.net">Presentational- and Container-Components</a> - this can be achieved easily with Redux.<br>
Your presentational component defines as props just what it&apos;s showing and not the source for a data aggregation.<br>
An example would be the <a href="https://github.com/MUnique/OpenMU/blob/d116008aab236110a06431174b066d6c2d5fd3f4/src/AdminPanel/content/js/components/LogNotifier.tsx?ref=munique.net">LogNotifier component</a>:</p>
<pre><code class="language-tsx">interface LogNotifierProps {
    showError: boolean;
    subscribe: (subscriber: any) =&gt; void;
    unsubscribe: (subscriber: any) =&gt; void;
}

class LogNotifier extends React.Component&lt;LogNotifierProps, {}&gt; { &#x2026; }
</code></pre>
<p>As you can see, the component itself doesn&apos;t define all log entries in the props to be able to figure out itself, if it should show a sign or not. Instead, it just defines <em>showError</em>. The <em>mapStateToProps</em> function which is used by the connect function of Redux, determines <em>showError</em>.</p>
<pre><code>const mapStateToProps = (state: ApplicationState) =&gt; {
    return {
        showError: anyEntry(state.logTableState, &quot;ERROR&quot;),
    };
}

const mapDispatchToProps = (dispatch: any) =&gt; {
    return {
        subscribe: (subscriber: any) =&gt; dispatch(logSubscribe(subscriber)),
        unsubscribe: (subscriber: any) =&gt; dispatch(logUnsubscribe(subscriber)),
    };
}

const anyEntry = (state: LogTableState, logLevel: string): boolean =&gt; {
    for (var i = state.entries.length - 1; i &gt;= 0; i--) {
        var entry = state.entries[i];

        if (logLevel.localeCompare(entry.Level.Name) === 0) {
            return true;
        }
    }

    return false;
}

export default connect(mapStateToProps, mapDispatchToProps)(LogNotifier);
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Migrated AdminPanel to Redux and TypeScript]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I finally finished the <a href="https://github.com/MUnique/OpenMU/pull/37?ref=munique.net">migration</a> of the <a href="https://github.com/MUnique/OpenMU/tree/master/src/AdminPanel?ref=munique.net">AdminPanel</a> from JS/JSX to TS/TSX and <a href="http://fluxxor.com/?ref=munique.net">fluxxor</a> to <a href="https://redux.js.org/?ref=munique.net">Redux</a>. I want to show you a short summary of pitfalls which I encountered and how I solved them.</p>
<h4 id="notusingnpm">Not using npm</h4>
<p>When working with JavaScript you almost can&apos;t get around</p>]]></description><link>https://munique.net/migrated-adminpanel-to-redux-and-typescript/</link><guid isPermaLink="false">6633f2d02fa90259941c3fbf</guid><category><![CDATA[OpenMU]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[React]]></category><category><![CDATA[Redux]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Thu, 14 Jun 2018 01:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2018/09/serverlist2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://munique.net/content/images/2018/09/serverlist2.png" alt="Migrated AdminPanel to Redux and TypeScript"><p>I finally finished the <a href="https://github.com/MUnique/OpenMU/pull/37?ref=munique.net">migration</a> of the <a href="https://github.com/MUnique/OpenMU/tree/master/src/AdminPanel?ref=munique.net">AdminPanel</a> from JS/JSX to TS/TSX and <a href="http://fluxxor.com/?ref=munique.net">fluxxor</a> to <a href="https://redux.js.org/?ref=munique.net">Redux</a>. I want to show you a short summary of pitfalls which I encountered and how I solved them.</p>
<h4 id="notusingnpm">Not using npm</h4>
<p>When working with JavaScript you almost can&apos;t get around using <a href="https://www.npmjs.com/?ref=munique.net">npm</a>. However, I&apos;m not a fan of including too many dependencies in the code and the npm makes it too easy to introduce them.<br>
My workflow of adding a new library is therefore a bit different.<br>
One thing to note is, that the TypeScript compiler uses the module system of SystemJS by default. The syntax is basically a new standard which will be implemented by future browsers so that the SystemJS lib will not be required anymore.</p>
<blockquote>
<p>Disclaimer: This post only includes a small part of the libraries which I had to include. For the full code, better look at the GitHub repository.</p>
</blockquote>
<h4 id="typescriptconfiguration">TypeScript Configuration</h4>
<p>To be able to use aliases in imports, like <code>import Redux from &quot;redux&quot;;</code> instead of defining the path of the library in every ts/tsx file, I have to add them manually at the <a href="https://github.com/MUnique/OpenMU/blob/d116008aab236110a06431174b066d6c2d5fd3f4/src/AdminPanel/tsconfig.json?ref=munique.net">tsconfig.json</a> in the paths:</p>
<pre><code class="language-json">  &quot;compilerOptions&quot;: {
        &quot;paths&quot;: {
          &quot;*&quot;: [ &quot;*&quot; ],
          &quot;react&quot;: [ &quot;Scripts/react/index&quot; ],
          &quot;redux&quot;: [ &quot;Scripts/redux/index&quot; ],
          ...
        }
   }
</code></pre>
<h4 id="runningthecode">Running the code</h4>
<p>To use the code at runtime, so that the browser is able to resolve and run, I had to add SystemJS and the generated code as scripts in the <a href="https://github.com/MUnique/OpenMU/blob/d116008aab236110a06431174b066d6c2d5fd3f4/src/AdminPanel/views/Home/index.sshtml?ref=munique.net">index.(ss)html</a>:</p>
<pre><code class="language-html">&lt;script src=&quot;@Path[&apos;~/content/js/system-production.js&apos;]&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;@Path[&apos;~/content/js/app.js&apos;]&quot;&gt;&lt;/script&gt;
</code></pre>
<p>The app.js was generated by the TypeScript compiler and is actually registering the modules/components at SystemJS, example:</p>
<pre><code class="language-javascript">// generated code at app.js:
System.register(&quot;components/App&quot;, [&quot;react&quot;, &quot;react-dom&quot;, &quot;react-redux&quot;, &quot;components/Layout&quot;, &quot;stores/store&quot;], function (exports_37, context_37) {&#x2026;}
</code></pre>
<p>This raises the question how SystemJS should know what &quot;redux&quot; or &quot;react&quot; means? We have to &quot;map&quot; them manually:</p>
<pre><code>&lt;script&gt;
            System.config({
              map: {
                &apos;react&apos;: &apos;@Path[&apos;~/content/js/react.js&apos;]&apos;,
                &apos;redux&apos;: &apos;@Path[&apos;~/content/js/redux.js&apos;]&apos;
              }
            });
&lt;/script&gt;
</code></pre>
<ul>
<li>To run the app, I&apos;m calling System.import on the <a href="https://github.com/MUnique/OpenMU/blob/d116008aab236110a06431174b066d6c2d5fd3f4/src/AdminPanel/content/js/components/App.tsx?ref=munique.net">App-Component</a>:</li>
</ul>
<pre><code class="language-javascript">System.import(&quot;components/App&quot;)
                .then(m =&gt; console.log(&apos;App module resolved&apos;))
                .catch(err =&gt; console.error(err));
</code></pre>
<p>This component directly calls ReactDOM.render:</p>
<pre><code class="language-typescript">// components/App.tsx:
import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import { Provider } from &quot;react-redux&quot;;
import { Layout } from &quot;./Layout&quot;;

import configureStore from &quot;../stores/store&quot;;

const store = configureStore(&#x2026;);

// This here is our entry point
ReactDOM.render(
    &lt;Provider store={store}&gt;
        &lt;Layout /&gt;
    &lt;/Provider&gt;, document.getElementById(&quot;content&quot;)
);
</code></pre>
<p>Voil&#xE0;, the app renders :)</p>
<h3 id="conclusion">Conclusion</h3>
<p>All in all, the setup was a bit tedious, because I had to read through all possible documentations. I found no &quot;boilerplate&quot; example of setting up an environment like mine (React &amp; Redux &amp; TypeScript in a VS project without npm). However, I think it was worth it. I&apos;m now very productive at extending the AdminPanel. Using types makes refactoring easier and obvious bugs are found faster by the compiler without running the code in the browser.<br>
When selecting new libraries I now have to look after TypeScript definition files, which is a bit tedious... another point which prevents me to add new dependencies without explicitly thinking about the consequences.</p>
<h3 id="whatsnext">What&apos;s next</h3>
<p>It seems like bootstrap is already &apos;old-school&apos; and material-ui is the new &apos;standard&apos; for React based web-apps. It looks great, but I&apos;ll have to see if it makes sense for the project.</p>
<p>Maybe I&apos;ll add some unit tests for the TypeScript code, too. As I read in some tutorials this seems to be easy :)</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[On item duplication exploits and how to prevent them]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hello,</p>
<p>first of all, if you&apos;re Webzen or a private MU Online server owner or developer, don&apos;t take this post as a threat. These item duplication examples which I will describe are known to me since many years. As you might imagine a lot of other</p>]]></description><link>https://munique.net/item-duplication-exploits/</link><guid isPermaLink="false">6633f2d02fa90259941c3fbd</guid><category><![CDATA[MU Online]]></category><category><![CDATA[OpenMU]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Wed, 09 May 2018 01:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Hello,</p>
<p>first of all, if you&apos;re Webzen or a private MU Online server owner or developer, don&apos;t take this post as a threat. These item duplication examples which I will describe are known to me since many years. As you might imagine a lot of other people may know this issues already, too. So, if your server still has no fix for them, I&apos;m very sorry.</p>
<p>I&apos;m trying to describe how item duplicate bugs worked at the original MU Online server, how they can prevented and how the OpenMU project tries to do that. As data coming from the game client shouldn&apos;t be trusted, all solutions I describe here should be implemented server-side, of course.</p>
<h4 id="basics">Basics</h4>
<h5 id="whatareitemduplicatesandhowaretheyused">What are item duplicates and how are they used</h5>
<p>It&apos;s probably the best known problem in the world of MMORPGs. Probably every MMORPG has items which are particularly valuable, so cheaters try to duplicate them to gain an advantage in the game over other players. Either to use them themselves, sell it to other players for in-game currencies or even to sell them for real money on the black market.<br>
In MU Online, these exploits were also used to upgrade or convert items without risk to lose them. For example, if you have some rare ingredients to create new rare and valuable items by using <em>chaos machine combinations</em>, you could create these endlessly.</p>
<h5 id="howareitemduplicatescreated">How are item duplicates created</h5>
<p>Basically, every item duplication trick which I know depends on the timing of saving (item) data. Additionally, there are no optimistic or pessimistic lock strategies which could detect if there was data saved for the same account before from another session. So, if you know when a server saves data, you can figure out how to exploit certain game actions.</p>
<h5 id="itempersistenceontheoriginalserver">Item persistence on the original server</h5>
<p>On the original server, when a player succeeds of getting an item duped in a non-persistent state, it can be easily turned to be persistent. As items are saved in big binary fields, their &quot;serial numbers&quot; are not automatically indexed or checked for uniqueness by the database. Instead, these checks have to be done at application level... which might have loopholes.<br>
It&apos;s also possible to scan the whole database for duplicated items periodically, but that&apos;s very time- and resource-consuming. Also this shouldn&apos;t run while the server is running, because the state in game servers might not be completely persistent. Otherwise, you may face false-positives, e.g. <em>Player A</em> is still saved with an item which somehow moved to <em>Player B</em>, but only the state of <em>Player B</em> is saved yet after the movement.<br>
Because of this delay, cheaters could have enough time to clean up traces of their duplicates, e.g. selling them to an NPC merchant or washing jewels with the <em>Lahap NPC</em>. Item duplicate bugs are still useful for cheaters even if they can just keep one copy of an item: They can duplicate items as backup before trying to upgrade them (fails might destroy an item) or using them to create rare items in the chaos machine.</p>
<h4 id="dupemethodexamples">Dupe method examples</h4>
<h5 id="logginginatthesametimewiththesameaccount">Logging in at the same time with the same account</h5>
<p>In a very early version of Mu Online, even before Atlans map arrived at GMO, you could log into an account twice at the same time if you did that synchronously enough. Because this bug is so old, it&apos;s probably fixed in all versions of Mu Online.<br>
It&apos;s the bug which brought all the maximized Excellent Brass Sets to Wigle and Maya realms. When logged in multiple times with the same account, you could do different actions to get duped items in the end:</p>
<ul>
<li>Trade the same item to two different characters.</li>
<li>Trade just to one different character, then disconnecting the account which traded, first.</li>
</ul>
<h6 id="possiblesolutions">Possible Solutions:</h6>
<ol>
<li>Thread-safe login server or session manager. Obviously, a thread-safe data structure or process is needed to flag an account to be logged-in. And at login stage, this state needs to be checked in a safe manner. An account should only be able to be logged in once at the same time and after all other previous session states are saved.</li>
<li>Usage of optimistic or pessimistic lock strategies.</li>
</ol>
<h5 id="usinglahapandtrade">Using Lahap and Trade</h5>
<p>This one is an exception - it&apos;s not based on timing of item saving, but on a fact about trading which is like an invitation to cheaters: Cancelling a trade resets the state of the whole inventory and it&apos;s items to the previous state - as you will see, only half-hearted.<br>
Back then, hackers opened a trade with some other character and at the same time used the Lahaps functions. The <em>Lahap NPC</em> is the guy which is able to pack and unpack jewels to save some inventory space. For example, instead of 30 single Jewel of Bless, you could pack it into one item. And to be able to use them later, it must able to unpack them again.<br>
The server didn&apos;t check if you had the <em>Lahap NPC</em> opened when you used these functions. So what the hackers did:</p>
<ul>
<li>First, having 10, 20 or 30 unpacked jewels in inventory</li>
<li>Opening a trade with another character</li>
<li>Sending a data packet to pack these jewels</li>
<li>Sending a trade cancel packet</li>
<li>Then you&apos;d still have both, the packed and the unpacked jewels.</li>
<li>(... repeat the steps until the inventory is full ...)</li>
</ul>
<p>The server obviously didn&apos;t expect that you gained items during trade and didn&apos;t remove them from its memory, when cancelling the trade ;-)<br>
This bug led to an insane amount of jewels on the market of global mu online - regular Items were being sold for tens of thousands of jewels.<br>
You could now argue, Webzen (or K2 Network) could&apos;ve just deleted these illegally created jewels - but that&apos;s not easy. When packing/unpacking, each jewel gets a new item Id assigned. You can&apos;t tell (without detailed logs) which ones were created illegally and which not. Another point is, this Lahap functions can be used to &quot;wash&quot; duplicated jewels when they were duplicated by other ways, because each &quot;created&quot; jewel or pack gets a new Id by it.</p>
<h6 id="possiblesolutions">Possible Solutions:</h6>
<ol>
<li>Allow Lahaps functions only when the <em>Lahap NPC</em> is opened.</li>
<li>Prevent opening NPC dialogs and trades to other players at the same time.</li>
<li>The inventory reset when cancelling a trade must be implemented correctly.</li>
<li>Keep track of the Ids of the packed jewels and reassign them after unpacking - of course that&apos;s a bit more complex to do.</li>
</ol>
<h5 id="forcedautomaticserverchanges">Forced automatic server changes</h5>
<p>I don&apos;t know if this is still working in GMO (I hope not :D), but it did at least until the end of the Season 6 era. And this issue is/was huge, really huge.<br>
When moving between some specific maps, the server urges the client to connect to another game server, e.g. when you move from <em>Lorencia</em> to <em>Loren Market</em> and vice-versa. The server saves the players data to the database after it has been disconnected. When connecting to the other game server, it loads this data from the database as well. Usually, the game client closes the first connection, before connecting to the new game server. But when you keep the first connection open as long as you can, there might be enough time to trade away some items (or do other things...) and to disconnect before the first connection closes which causes the old data to be written to the database. Example:</p>
<ul>
<li><em>Player A</em> moves from Map <em>Lorencia</em> to <em>Loren Market</em>
<ul>
<li>The server sends a server change request with an authentication token to the players game client over <em>connection 1</em>.</li>
<li>The player keeps the <em>connection 1</em> open (e.g. with firewall rules or a proxy program).</li>
<li>The game client connects to the server of <em>Loren Market</em> using the authentication token, we call it <em>connection 2</em></li>
</ul>
</li>
<li><em>Player A</em> trades an item to <em>Player B</em></li>
<li><em>Player A</em> leaves the game, so that <em>connection 2</em> closes.</li>
<li><em>Player A</em> closes <em>connection 1</em> or waits until the server closes it by itself</li>
</ul>
<p>Can you spot the issue? Because the server saves the account data after a connection closes, the data saved by closing <em>connection 2</em> is getting overwritten by the data saved by closing of <em>connection 1</em>. Items which got traded exist now at the account data of <em>Player A</em> and <em>Player B</em>.</p>
<h6 id="solutions">Solutions:</h6>
<p>Again, you could fix this issue in several ways:</p>
<ol>
<li>Send out the authentication token AFTER the data got saved on the database by the first game server</li>
<li>Right after sending the authentication token, close the connection</li>
<li>Usage of optimistic or pessimistic lock strategies</li>
</ol>
<h4 id="conclusionandgeneralsolutions">Conclusion and general solutions</h4>
<p>As you can see, the original Mu Online server is or was pretty vulnerable when it comes to item duplications. I think that&apos;s because of its whole design and how it&apos;s saving item data. I don&apos;t think these were all existing issues here, there might be dozens of others...</p>
<p>The OpenMU project tries to be less vulnerable to these kind of issues. I try to describe, how.</p>
<h5 id="itemtableandids">Item table and IDs</h5>
<p>We try to prevent persistent duplications by saving items in an own table, where each item has its own row which is identifiable by a GUID as primary key. Therefore, there can only be one item with the same Id and only assigned to one account. So from a persistence point of view, we&apos;re safe. Cheaters would have a lot less time to clean up their traces of non-persistent duplications.</p>
<h5 id="threadsafeloginserver">Thread-safe Login Server</h5>
<p>Of course, this project has a <a href="https://github.com/MUnique/OpenMU/blob/f9ff453e9078d70a512a0a502f29f4be9458bbb9/src/LoginServer/LoginServer.cs?ref=munique.net">thread-safe login server</a>. One account can only be connected to one server at the same time. Of course, setting an account offline is done <a href="https://github.com/MUnique/OpenMU/blob/f9ff453e9078d70a512a0a502f29f4be9458bbb9/src/GameServer/GameServer.cs?ref=munique.net#L400">after saving the data</a>.</p>
<h5 id="noforcedautomaticserverchangesthroughtheclient">No forced automatic server changes through the client</h5>
<p>To minimize the attack vector, we just don&apos;t support this feature. Even if we need to scale-out to separate game servers for certain maps, we&apos;ll keep the initial connection and manage this all internally. See also <a href="https://gameserverarchitecture.com/2015/10/pattern-distributed-network-connections/?ref=munique.net">https://gameserverarchitecture.com/2015/10/pattern-distributed-network-connections/</a>.</p>
<h5 id="statemachines">State machines</h5>
<p>Each player holds a thread-safe <a href="https://github.com/MUnique/OpenMU/blob/f9ff453e9078d70a512a0a502f29f4be9458bbb9/src/GameLogic/StateMachine.cs?ref=munique.net">state machine</a> which allows or prevents the transition to a requested state. For example, when opening a <em>Lahap NPC</em> dialog it <a href="https://github.com/MUnique/OpenMU/blob/f9ff453e9078d70a512a0a502f29f4be9458bbb9/src/GameLogic/PlayerActions/TalkNpcAction.cs?ref=munique.net#L29">requests</a> to change the state to <em><a href="https://github.com/MUnique/OpenMU/blob/f9ff453e9078d70a512a0a502f29f4be9458bbb9/src/GameLogic/PlayerState.cs?ref=munique.net">PlayerState</a>.NpcDialogOpened</em>. The state machine only allows it if there is no trade open and the current state is <em>PlayerState.EnteredWorld</em>.</p>
<h5 id="todoitemhistorylog">To do: Item history log</h5>
<p>It would be nice to have some kind of log about each item, so you could find out easily if an item was duplicated or generated in an illegal way.<br>
There could be automatisms which could check the plausibility of an item history, so manual interaction is not required.<br>
For example, when selling an item to a NPC merchant or using it in a chaos combination, the server could check if the item was sold or destroyed before.</p>
<h5 id="todoreproducibleidsforgenerateditems">To do: Reproducible Ids for generated items</h5>
<p>Generated items (e.g. by chaos machine) with the same identifiable valuable ingredients should get the same Id assigned. So when cheaters use the same ingredients over and over to create new items out of them, the resulting items should have the same Id. This way, the database could instantly tell us if something is fishy with a particular item when the session is saved. When multiple valuable ingredients are used, a hash value of all of their Ids could be built, otherwise you could just use the Id of the most valuable ingredients.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[About game server architecture patterns]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hey there,</p>
<p>I found a really nice blog about game server architectures: <a href="https://gameserverarchitecture.com/?ref=munique.net">https://gameserverarchitecture.com</a></p>
<p>If I would categorize the original MU Online game server into one of the mentioned architecture patterns, it&apos;s probably mostly the <a href="https://gameserverarchitecture.com/2015/10/pattern-monolithic-architecture/?ref=munique.net">monolithic</a> one, with some slight signs of the <a href="https://gameserverarchitecture.com/2015/10/pattern-map-centric-game-server/?ref=munique.net">map centric pattern</a> in</p>]]></description><link>https://munique.net/about-game-server-architecture-patterns/</link><guid isPermaLink="false">6633f2d02fa90259941c3fbc</guid><category><![CDATA[MU Online]]></category><category><![CDATA[OpenMU]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Wed, 28 Mar 2018 01:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Hey there,</p>
<p>I found a really nice blog about game server architectures: <a href="https://gameserverarchitecture.com/?ref=munique.net">https://gameserverarchitecture.com</a></p>
<p>If I would categorize the original MU Online game server into one of the mentioned architecture patterns, it&apos;s probably mostly the <a href="https://gameserverarchitecture.com/2015/10/pattern-monolithic-architecture/?ref=munique.net">monolithic</a> one, with some slight signs of the <a href="https://gameserverarchitecture.com/2015/10/pattern-map-centric-game-server/?ref=munique.net">map centric pattern</a> in case of &apos;Loren valley&apos;, &apos;Crywolf&apos; etc. In case of the latter maps, the game client however starts a new connection to another server and is not using something like <a href="https://gameserverarchitecture.com/2015/10/pattern-distributed-network-connections/?ref=munique.net">distributed network connections</a>. Because of that, the player is noticing major lags when the map change forces the game client to connect to another game server.</p>
<p>For the OpenMU project, I will probably stick to the monolithic approach for now. MU Online is a game which doesn&apos;t require that much resources, so I think one physical server could easily handle thousands of players with todays hardware. However, I already did some changes which would allow to switch to a more map-centric architecture. For example, a player sees himself always with the same fixed player id and object ids are managed by the game maps.</p>
<p>Of course, the <a href="https://gameserverarchitecture.com/2015/10/pattern-map-centric-game-server/?ref=munique.net">map centric pattern</a> would probably be perfectly suited for MU Online and would allow to run a server efficiently in the cloud. You could start with one or two (WorldServer + 1 AreaServer) small and cheap servers and depending on the load, you could add or remove AreaServers dynamically at runtime. So for example, instead of spending like 50 &#x20AC; a month for a fully dedicated server, you could start with one to three cheap 3 &#x20AC; servers and add more servers on-demand.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Loading complex data with PostgreSQL JSON functions]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h3 id="problem">Problem</h3>
<p>With an extension of initialization data, where I added definitions of NPCs and Monsters and their spawn points to about 10 game maps, I noticed that the time to start the server went up dramatically - from ~13 seconds to over 1 minute. The reason was, that my strategy</p>]]></description><link>https://munique.net/loading-complex-data-with-postgresql-json-functions/</link><guid isPermaLink="false">6633f2d02fa90259941c3fbb</guid><category><![CDATA[C#]]></category><category><![CDATA[PostgreSQL]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Thu, 11 Jan 2018 01:00:00 GMT</pubDate><media:content url="https://munique.net/content/images/2018/09/PostgreSQL.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h3 id="problem">Problem</h3>
<img src="https://munique.net/content/images/2018/09/PostgreSQL.jpg" alt="Loading complex data with PostgreSQL JSON functions"><p>With an extension of initialization data, where I added definitions of NPCs and Monsters and their spawn points to about 10 game maps, I noticed that the time to start the server went up dramatically - from ~13 seconds to over 1 minute. The reason was, that my strategy to load the whole game configuration at the start at the server led to almost 2000 SQL queries. As the configuration data is not yet complete, you can already guess that if nothing had been changed, the loading time would ramp up further, probably to 10 minutes or more.</p>
<h3 id="idea">Idea</h3>
<p>I had created a <a href="https://github.com/MUnique/OpenMU/issues/10?ref=munique.net">GitHub issue</a> and came up with a nice approach. The idea is to load the whole object graph of the GameConfiguration with just one query. PostgreSQL has <a href="https://www.postgresql.org/docs/current/static/functions-json.html?ref=munique.net#FUNCTIONS-JSON-CREATION-TABLE">functions to build JSON</a> for the query result, even very complex ones with deeply nested subqueries. So I got the idea that I could build queries based on the entity framework meta model of the GameConfiguration class. The result of the query can then get de-serialized to a GameConfiguration object and if possible, added to the change tracker of the entity framework. EntityFrameworkCore supports adding objects (and their child objects) to it&apos;s context from the outside. And we could also tell the context that it should treat this data as &apos;unchanged&apos; - so we could still do changes to the object and EF Core would figure out what needs to be changed on the database to save them. Best of both worlds, if all goes well :)</p>
<h3 id="implementation">Implementation</h3>
<h4 id="sqlquerybuilding">SQL Query Building</h4>
<p>To create the query, I pass a IEntityType (meta data about the entity type) of GameConfiguration to the <a href>JsonQueryBuilder</a>. It will programmatically build up a complex SQL query which uses PostgreSQL JSON functions. The resulting query has a size of about 18 KB, so it&apos;s too big to show it in this blog post. It looks like this:</p>
<pre><code>select result.&quot;Id&quot; &quot;$id&quot;, result.&quot;Id&quot;, row_to_json(result) as GameConfiguration
from (
    select a.&quot;Id&quot;, a.*, (
       -- additional subqueries which return json of collections and other navigation properties
    )
    from config.&quot;GameConfiguration&quot; a
) result;
</code></pre>
<p>The process to create this query is actually pretty fast (~10 ms) and we could cache the result as the query would never change after the program has been started.<br>
The result of the query is currently a JSON string which is about 21 MB in size. I suspect there is some garbage in it, but at the moment it&apos;s fast enough - room for improvement, however. The query returns the data in a little bit under 2 seconds on my system.</p>
<h4 id="deserializing">De-serializing</h4>
<p>I&apos;m using Newtonsoft.Json in version 9 to de-serialize the JSON back to an object. One de-serialize call takes about ~600 ms, which is also not so bad if you look at this amount of data. However, we need two calls to get the final result, to resolve all references.</p>
<h5 id="bytearrays">Byte Arrays</h5>
<p>I needed to write a <a href="https://github.com/MUnique/OpenMU/blob/3023e23fe98b8de224c5c9324d6ce8433c0d39e0/src/Persistence/EntityFramework/Json/BinaryAsHexJsonConverter.cs?ref=munique.net">custom JsonConverter</a> to de-serialize byte arrays. Newtonsoft.Json expects a base64-encoded string. However, PostgreSQL gives us the data in a hexadecimal encoded string.</p>
<h5 id="referenceresolving">Reference resolving</h5>
<p>To prevent that every object is serialized over and over again, I use $ref-objects to reference these objects by their id (GUID). To resolve these objects, there is a <a href="https://github.com/MUnique/OpenMU/blob/3023e23fe98b8de224c5c9324d6ce8433c0d39e0/src/Persistence/EntityFramework/Json/IdReferenceResolver.cs?ref=munique.net">resolver</a>. Whenever Newtonsoft.Json finds an object with &quot;$id&quot; as first property, it adds it to the resolver. However, it reads the JSON just one time in forward direction, so a reference to an object which was not yet added to the resolver yet, can&apos;t be resolved. The object model of GameConfiguration contains some circular references, so this is actually happening. The current solution is, that we de-serialize the JSON two times - this leads to other problems as you will see below.</p>
<h3 id="remainingproblems">Remaining problems</h3>
<h4 id="changetracker">Change Tracker</h4>
<p>We need to de-serialize the JSON of the GameConfiguration twice, because the document contains circular references which can&apos;t be resolved otherwise. The first run is just there to pick up all objects. In the second run these objects are used in the <a href="https://github.com/MUnique/OpenMU/blob/3023e23fe98b8de224c5c9324d6ce8433c0d39e0/src/Persistence/EntityFramework/Json/IdReferenceResolver.cs?ref=munique.net">IdReferenceResolver</a> to resolve the $ref-objects.<br>
So in the end the resulting GameConfiguration might contain objects with the same id but being a different object instance. However, the change tracker of EF Core can only keep a look at one instance per id. As a result, an exception is thrown when you try to add two different instances with the same id.<br>
So for now, the DbContext to load the game server configuration doesn&apos;t track changes anymore. That&apos;s not so much of a problem at the moment, as we don&apos;t modify it.</p>
<p>One solution could be to replace all instances of the first run, which still sit in the GameConfiguration object, by the &quot;new&quot; one which has been created in the second run.</p>
<p>Newtonsoft.Json is pretty limited in this particular case. It would be nice to be able to postpone reference resolving until the whole document has been read.</p>
<h4 id="serializationofbackreferences">Serialization of back references</h4>
<p>See also <a href="https://github.com/MUnique/OpenMU/issues/11?ref=munique.net">Issue #11</a> - when we fix this, we can expect further performance gains and we&apos;ll be able to use it to load Account objects. These are candidates for getting very complex, too.</p>
<h3 id="summary">Summary</h3>
<p>The JSON functions of PostgreSQL are a great way to load complex object graphs with just one roundtrip to the database. We have some minor issues left, but I&apos;m sure we can solve them. Additionally, there is still a lot of performance to gain because the query retrieves obviously too much data.<br>
If we manage to optimize that, we keep all benefits of a relational database and still only need one roundtrip to load complex data - so we have no reason to switch to a document database, yet :)</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[MUnique OpenMU Network Analyzer]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hey people,<br>
today I release a little tool to analyze network traffic between server and client which use the mu protocol.<br>
The cool thing is, the packet structures are defined in xml files, so it can be extended like you wish. Changes on this xml files also work on-the-fly -</p>]]></description><link>https://munique.net/munique-openmu-network-analyzer/</link><guid isPermaLink="false">6633f2d02fa90259941c3fba</guid><category><![CDATA[Release]]></category><category><![CDATA[OpenMU]]></category><category><![CDATA[C#]]></category><category><![CDATA[MU Online]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Tue, 02 Jan 2018 01:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Hey people,<br>
today I release a little tool to analyze network traffic between server and client which use the mu protocol.<br>
The cool thing is, the packet structures are defined in xml files, so it can be extended like you wish. Changes on this xml files also work on-the-fly - the tool will reload them after every file change.</p>
<p>For more details, visit <a href="https://github.com/MUnique/OpenMU/tree/master/src/Network/Analyzer?ref=munique.net">GitHub</a>.<br>
You can find compiled binaries here: <a href="https://github.com/MUnique/OpenMU/releases/download/v.0.1.6/MUnique.OpenMU.Network.Analyzer.v.0.1.6.zip?ref=munique.net">MUnique.OpenMU.Network.Analyzer.v.0.1.6.zip</a></p>
<p>Screenshot:<br>
<img src="https://munique.net/content/images/2018/09/analyzer.png" alt="analyzer" loading="lazy"></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[A closer look at the MU Online packet encryption]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>in the past days I wondered why some parts of the encryption and decryption keys for the network packet encryption (aka SimpleModulus) are actually the same. So I tried to dig a bit in the SimpleModulus algorithm and the usage of these keys.</p>
<p>Lets see how this keys (e.g.</p>]]></description><link>https://munique.net/a-closer-look-at-the-mu-online-packet-encryption/</link><guid isPermaLink="false">6633f2d02fa90259941c3fb9</guid><category><![CDATA[OpenMU]]></category><category><![CDATA[MU Online]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[Sven]]></dc:creator><pubDate>Tue, 19 Dec 2017 01:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>in the past days I wondered why some parts of the encryption and decryption keys for the network packet encryption (aka SimpleModulus) are actually the same. So I tried to dig a bit in the SimpleModulus algorithm and the usage of these keys.</p>
<p>Lets see how this keys (e.g. S-&gt;C) look like...</p>
<p>Encryption Key: <strong>73326, 109989, 98843, 171058</strong>, <em>13169, 19036, 35482, 29587</em>, <strong>62004, 64409, 35374, 64599</strong><br>
Decryption Key: <strong>73326, 109989, 98843, 171058</strong>, <em>18035, 30340, 24701, 11141</em>, <strong>62004, 64409, 35374, 64599</strong></p>
<p>These keys are consisting of twelve 32-bit integers. What can you see is, that the first and the last 4 integers are identical for the encryption and decryption key. Additionally these keys use almost only 16 bits - only the first 4 numbers are actually over it. One of these keys is used by the server to encrypt outgoing network packets, and the other is used by the client to decrypt incoming network packets. So it&apos;s some kind of a private/public key pair.</p>
<p>So, I had a closer look at the <a href="https://github.com/MUnique/OpenMU/blob/a1b07df0d18637c2f8ade1ccc859636715a66d89/src/Network/Encryptor.cs?ref=munique.net#L145">encryption code</a> which uses one of these keys:</p>
<pre><code>this.RingBuffer[0] = ((keys[8] ^ this.CryptBuffer[0]) * keys[4]) % keys[0];
this.RingBuffer[1] = ((keys[9] ^ (this.CryptBuffer[1] ^ (this.RingBuffer[0] &amp; 0xFFFF))) * keys[5]) % keys[1];
this.RingBuffer[2] = ((keys[10] ^ (this.CryptBuffer[2] ^ (this.RingBuffer[1] &amp; 0xFFFF))) * keys[6]) % keys[2];
this.RingBuffer[3] = ((keys[11] ^ (this.CryptBuffer[3] ^ (this.RingBuffer[2] &amp; 0xFFFF))) * keys[7]) % keys[3];
</code></pre>
<p>What you can see here is, that the index of 8-11 is used for a XOR-Operation (so we call it XOR-Key x), the index 4-7 is used for a multiplication (Encryption-Key k) and the first index of 0-3 is used for a modulus operation (Modulus-Key m). Because each encryption key uses at most only 16 bits, there are 4 chained 16-bit encryptions, which looks like it&apos;s easy to break just with a bruteforce attack.</p>
<p>The <a href="https://github.com/MUnique/OpenMU/blob/a1b07df0d18637c2f8ade1ccc859636715a66d89/src/Network/Decryptor.cs?ref=munique.net#L147">decryption code</a> looks similar:</p>
<pre><code>this.CryptBuffer[0] = (ushort)(keys[8] ^ ((this.RingBuffer[0] * keys[4]) % keys[0]));
this.CryptBuffer[1] = (ushort)(keys[9] ^ ((this.RingBuffer[1] * keys[5]) % keys[1]) ^ (this.RingBuffer[0] &amp; 0xFFFF));
this.CryptBuffer[2] = (ushort)(keys[10] ^ ((this.RingBuffer[2] * keys[6]) % keys[2]) ^ (this.RingBuffer[1] &amp; 0xFFFF));
this.CryptBuffer[3] = (ushort)(keys[11] ^ ((this.RingBuffer[3] * keys[7]) % keys[3]) ^ (this.RingBuffer[2] &amp; 0xFFFF));
</code></pre>
<p>For the sake of simplification, we can ignore the xor-key as it plays no role, because it&apos;s the same for both:</p>
<blockquote>
<p>encrypt(input<sub>dec</sub> ) -&gt; input<sub>dec</sub> * k<sub>enc</sub> (mod m)</p>
</blockquote>
<blockquote>
<p>decrypt(input<sub>enc</sub> ) -&gt; input<sub>enc</sub> * k<sub>dec</sub> (mod m)</p>
</blockquote>
<p>This encryption is based on <a href="https://en.wikipedia.org/wiki/Modular_multiplicative_inverse?ref=munique.net">modular multiplicative inverses</a>.<br>
One requirement, that this encryption can work like this at all, is that <em>k</em> and <em>m</em> are always <a href="https://en.wikipedia.org/wiki/Coprime_integers?ref=munique.net">coprime</a> - which is the case for all our example key values.</p>
<p>With this knowledge we can build the following formula:</p>
<blockquote>
<p>k<sub>enc</sub> * k<sub>dec</sub> (mod m) = 1</p>
</blockquote>
<p>So when we know one of the two keys, we can calculate the other one.</p>
<blockquote>
<p>Did I forget to mention Webzen didn&apos;t even try hard to hide these keys? The game client has two files with them in it&apos;s data folder, Enc1.dat and Dec2.dat - of course this files are encrypted by another weak XOR-Encryption :-)</p>
</blockquote>
<h4 id="example">Example:</h4>
<blockquote>
<p>13169 * k<sub>dec</sub> (mod 73326) = 1</p>
</blockquote>
<p>To solve this we could use the extended euclidean algorithm but our computer is also fast enough to find k<sub>dec</sub> with the slower naive algorithm which tests every possible value of k<sub>dec</sub> from 0 to (m-1):</p>
<pre><code>private bool FindDecryptKey(uint modulusKey, uint encryptKey, out uint decryptKey)
{
    decryptKey = 0;
    for (uint i = 0; i &lt; modulusKey; i++)
    {
        if (encryptKey * i % modulusKey == 1)
        {
            decryptKey = i;
            return true;
        }
    }

    return false;            
}
</code></pre>
<p>When we call it with our example values, we get:<br>
<img src="https://munique.net/content/images/2018/09/key.PNG" alt loading="lazy"></p>
<p>We can also use the same function to get the encryption key for a known decryption key - so this encryption algorithm is totally insecure.</p>
<h4 id="conclusion">Conclusion</h4>
<p>To protect the network packets from the prying eyes of a hacker, this encryption algorithm is not good enough. Anyone who wants to run a MU online server in production should really use something else.</p>
<blockquote>
<p>My little Webzen rant: Not without reason, security experts say to not roll your own crypto - Webzen developers have seemed to ignore this advice. Not long after they introduced it (somewhere around 2003~2004), hacks were popping up and server developers had reverse engineered the encryption algorithm. However, Webzen didn&apos;t respond accordingly - they kept using it at official game servers until the end of 2011 when the version of ex700 arrived. No wonder, the servers were full of cheaters ;-)</p>
</blockquote>
<h5 id="whatthismeansformyproject">What this means for my project?</h5>
<p>At the moment, the OpenMU.Network project only contains the keys which are used on the server side. I thought that would be a good idea from keeping hackers away, but it also limits the usage of the project for other kind of applications (e.g. event bots run by server owners or for MU server development tools like network MITM proxies). So, I will extend the project by the other part of the keys (and a key generator, of course) soon.<br>
As I gained some better understanding of the algorithm I&apos;ll also try to make the code more understandable.</p>
<p>Long term, I also think about adding the subsequent encryption layer which was introduced with version of ex700, aka &quot;Packet Twister&quot;, which source code is also already (partially) known to the private server development community.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>