{"id":2196,"date":"2022-02-14T22:29:28","date_gmt":"2022-02-15T03:29:28","guid":{"rendered":"https:\/\/badecho.com\/?p=2196"},"modified":"2023-03-15T01:00:49","modified_gmt":"2023-03-15T06:00:49","slug":"vision","status":"publish","type":"post","link":"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/","title":{"rendered":"Vision: An Omnified Game Overlay Platform"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview.png\" alt=\"Vision: An Omnified Game Overlay Platform\" class=\"wp-image-2209\" width=\"856\" height=\"456\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview-300x160.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview-768x409.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview-480x256.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/figure><\/div>\n\n\n\n<p><em>Vision<\/em> is a game overlay platform that provides visualized data for an <a href=\"https:\/\/badecho.com\/index.php\/what-is-omnified\/\" target=\"_blank\" rel=\"noreferrer noopener\">Omnified<\/a> game; moreover, it is the culmination of the Omnified experiment as a whole. It is very much the cherry on top of all the other technologies created during this quest of ours to hack games into being near impossible to beat.<\/p>\n\n\n\n<p>For every Omnified game hacked, new techniques and challenging gameplay systems were developed. The complexity of these gameplay systems grew to a point where it became desirable to have a way to communicate to the player (or, luck willing, <a href=\"https:\/\/twitch.tv\/omni\" target=\"_blank\" rel=\"noreferrer noopener\">a captive audience<\/a>) everything that was happening behind the scenes.<\/p>\n\n\n\n<p>This desire to be able to see the inner machinations of these Omnified game systems brought about a nascent messaging system. This more or less worked by displaying messages <a href=\"https:\/\/twitch.tv\/omni\" target=\"_blank\" rel=\"noreferrer noopener\">on stream<\/a> through primitive window captures of various console windows busy monitoring the tail output of log files. Not ideal!<\/p>\n\n\n\n<p>In order to truly provide an evocative and pleasing gameplay viewing experience, work on <em>Vision<\/em> soon began. Built on <strong>C#<\/strong> and <strong>WPF<\/strong>, it was meant to and succeeds in providing a much more visually pleasing presentation of data than what a hodgepodge of ugly console windows gives you. It also was something I wanted to have in a completed state before moving on to other projects. <\/p>\n\n\n\n<p>And in a completed state, <strong>it is<\/strong>! Such a work deserves a bit of a writeup, and this article here will serve as just that.<\/p>\n\n\n\n<h2>The Hacks<\/h2>\n\n\n\n<p><em>Vision<\/em> does not exist within a vacuum. At the highest level, there are two components to the system providing the on-screen experience to the user: 1) the <em>Vision<\/em> platform, which is a consumer of game data, and 2) the Omnified hacking framework injected into the binary game target, which is the producer of said game data.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/SystemComponents.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/SystemComponents.png\" alt=\"Omnified code injected into a binary, exporting captured game data into message files that are parsed by Vision and its modules.\" class=\"wp-image-2214\" width=\"856\" height=\"456\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/SystemComponents.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/SystemComponents-300x160.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/SystemComponents-768x409.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/SystemComponents-480x256.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/a><figcaption>Omnified code injected into a binary, exporting captured game data into message files that are parsed by Vision and its modules.<\/figcaption><\/figure><\/div>\n\n\n\n<p>The data displayed by <em>Vision<\/em> originates from data collection efforts of Omnified game-neutral systems (such as the <a href=\"https:\/\/badecho.com\/index.php\/2020\/10\/19\/apocalypse-system\/\" target=\"_blank\" rel=\"noreferrer noopener\">Apocalypse<\/a> and <a href=\"https:\/\/badecho.com\/index.php\/2021\/06\/18\/predator-system\/\" target=\"_blank\" rel=\"noreferrer noopener\">Predator systems<\/a>) injected in the game&#8217;s binary. Data collection code consists of assembly instructions that copy the desired data to known symbolic addresses in memory.<\/p>\n\n\n\n<p>The data is collected during the course of the relevant game-neutral system&#8217;s standard activities, and can be found at the appropriate system&#8217;s assembly file location in the <a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/tree\/master\/framework\/systems\" target=\"_blank\" rel=\"noreferrer noopener\">Omnified hacking framework&#8217;s system directory<\/a>. Since the only data we&#8217;re interested in visualizing is the data we&#8217;re affecting, almost all the data required for a proper <em>Vision<\/em> experience can be found in these game-changing routines.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/DataCollectionCode.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/DataCollectionCode.png\" alt=\"apocalypse.asm: Here is some example data collection code found within the Apocalypse system's code.\" class=\"wp-image-2219\" width=\"856\" height=\"456\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/DataCollectionCode.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/DataCollectionCode-300x160.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/DataCollectionCode-768x409.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/DataCollectionCode-480x256.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/a><figcaption>Here is some example data collection code found within the <a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/framework\/systems\/apocalypse.asm#L429\" target=\"_blank\" rel=\"noreferrer noopener\">Apocalypse system&#8217;s code<\/a>.<\/figcaption><\/figure><\/div>\n\n\n\n<h6>Data needs to be communicated<\/h6>\n\n\n\n<p>Simply storing all this data in memory is not enough for <em>Vision<\/em>&#8216;s purposes; rather, we must also organize the data into reportable chunks (i.e., <em>messages<\/em>) in a format understood by <em>Vision<\/em>. Fortunately for us, this is exactly what the Omnified hacking framework&#8217;s messaging library does.<\/p>\n\n\n\n<p>While the injected assembly code is modifying the behavior of the game, higher level functions are being executed in a number of Lua modules that are also a part of the Omnified hacking framework. These are responsible for things such as periodically checking our known &#8220;data dump&#8221; locations and spooling them into publishable messages.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/MessagingCode.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/MessagingCode.png\" alt=\"messages.lua: Here is some example messaging code found within the Omnified hacking framework.\" class=\"wp-image-2223\" width=\"856\" height=\"456\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/MessagingCode.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/MessagingCode-300x160.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/MessagingCode-768x409.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/MessagingCode-480x256.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/a><figcaption>Here is some example messaging code found within the <a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/framework\/messaging.lua#L81\" target=\"_blank\" rel=\"noreferrer noopener\">Omnified hacking framework<\/a>.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Schemas for the various message formats are defined with Lua code and found in files such as <em><a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/framework\/apocalypseMessages.lua\" target=\"_blank\" rel=\"noreferrer noopener\">apocalypseMessages.lua<\/a><\/em> and <em><a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/framework\/statisticMessages.lua\" target=\"_blank\" rel=\"noreferrer noopener\">statisticMessages.lua<\/a><\/em>. The message data objects are constructed and encoded into JSON by the Omnified messaging library found in <em><a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/framework\/messaging.lua\" target=\"_blank\" rel=\"noreferrer noopener\">messaging.lua<\/a><\/em>.<\/p>\n\n\n\n<p>Once all is said and done, we end up with data written to <em>message files<\/em>, and this data needs some damn visualization.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/omnDab3_224.png\" alt=\"Omni Dab!\" class=\"wp-image-2225\" width=\"56\" height=\"56\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/omnDab3_224.png 224w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/omnDab3_224-150x150.png 150w\" sizes=\"(max-width: 56px) 100vw, 56px\" \/><\/figure><\/div>\n\n\n\n<p>That&#8217;s when the <em>Vision<\/em> platform takes over.<\/p>\n\n\n\n<h2>The Platform<\/h2>\n\n\n\n<p><em>Vision<\/em> and all of its assorted components are Omnified products that can be found within the <a href=\"https:\/\/github.com\/BadEcho\/core\" target=\"_blank\" rel=\"noreferrer noopener\">Bad Echo technologies source repository<\/a>. <\/p>\n\n\n\n<p>It consists of a main application and a collection of plugins (referred to as <em>Vision<\/em> modules), each of which is responsible for displaying a different type of Omnified game data. The <em>Vision<\/em> application hosts these modules, laying them out on a transparent overlay which it places on top of a game.<\/p>\n\n\n\n<p>The application runs on Windows Presentation Foundation, augmented by <a href=\"https:\/\/github.com\/BadEcho\/core\/tree\/master\/src\/Presentation\" target=\"_blank\" rel=\"noreferrer noopener\">the Bad Echo Presentation framework.<\/a> A fully transparent window is created, covering the designated screen (where the game is running) and configured to pass through all input events (we don&#8217;t want to interfere with the game!).<\/p>\n\n\n\n<p>On this overlay window, custom layout functionality is present, which attaches detected <em>Vision<\/em> modules to particular places (referred to as <em>anchor point locations<\/em>) onto the screen. Different games consume different amounts of screen real estate, so the layout system for the modules was designed to be sufficiently configurable.<\/p>\n\n\n\n<p>In addition to managing the physical orchestration of the visual data being presented, the host application also provides the messaging system that feeds the individual modules with the data they are to display. While the application lacks any understanding of how to parse the message data, it does know takes care of all the details in regards to the monitoring and retrieval of the data deemed relevant for display.<\/p>\n\n\n\n<h3>Anchor Point Layout<\/h3>\n\n\n\n<p><em>Vision<\/em>&#8216;s main window is essentially a collection of module-derived views; these views must be laid out in a sensible fashion, lest they impede us from being able to see important &#8220;vanilla&#8221; game information. Keeping in line with this desire, the notion of anchor point locations, and the attachment of said module views to them, is introduced by <em>Vision<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/AnchorPointLocations.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/AnchorPointLocations.png\" alt=\"The various anchor points as they would appear on your screen that Vision attaches its modules to.\" class=\"wp-image-2229\" width=\"856\" height=\"456\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/AnchorPointLocations.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/AnchorPointLocations-300x160.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/AnchorPointLocations-768x409.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/AnchorPointLocations-480x256.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/a><figcaption>The various anchor points as they would appear on your screen that Vision attaches its modules to.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Windows Presentation Foundation comes with a number of <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.windows.controls.panel?view=windowsdesktop-6.0\" target=\"_blank\" rel=\"noreferrer noopener\">stock layout panels<\/a>; the layout pictured above may look, even to the most neophyte of WPF developers, like something easily achievable with a <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.windows.controls.grid?view=windowsdesktop-6.0\" target=\"_blank\" rel=\"noreferrer noopener\"><code>Grid<\/code><\/a>. However, this is not actually the case, as the ability to attach multiple elements to the same anchor point location became a requirement during <em>Vision<\/em>&#8216;s development.<\/p>\n\n\n\n<p>This flies in the face of how a <code>Grid<\/code> generally operates, whose specific row and column composition is typically defined declaratively in code and consequently set in stone. Using a <code>Grid<\/code> for <em>Vision&#8217;s<\/em> root layout panel would require dynamic column and row definition hackery; this would be a rather brutish way to go about doing things.<\/p>\n\n\n\n<h6>A custom layout panel is required<\/h6>\n\n\n\n<p>If multiple elements need to be able to be attached to the same anchor point location, then what we really need is a custom layout panel which not only recognizes anchor point location designations, but is also able to arrange elements attached to one anchor point location independently (space willing) from elements attached to another.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2020\/09\/omnigasmbigger.png\" alt=\"New schedule!\" class=\"wp-image-290\" width=\"56\" height=\"56\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2020\/09\/omnigasmbigger.png 224w, https:\/\/badecho.com\/wp-content\/uploads\/2020\/09\/omnigasmbigger-150x150.png 150w\" sizes=\"(max-width: 56px) 100vw, 56px\" \/><\/figure><\/div>\n\n\n\n<p>To accomplish all of this, the <a href=\"https:\/\/github.com\/BadEcho\/vision\/blob\/master\/src\/Vision\/Controls\/AnchorPointPanel.cs\" target=\"_blank\" rel=\"noreferrer noopener\"><code>AnchorPointPanel<\/code> <\/a>custom layout panel was created. Not having written many of my own custom layout panels before (I&#8217;m sure less than 1% of people who have developed with WPF ever have made their own), it was a bit of an undertaking; however, I was very pleased with the results, which more than sufficiently satisfied <em>Vision<\/em>&#8216;s layout requirements.<\/p>\n\n\n\n<p>A proper layout system is important and all, but only if you actually have something to display to the user. That&#8217;s where <em>Vision<\/em>&#8216;s module extensibility system comes into play.<\/p>\n\n\n\n<h3>Module Extensibility System<\/h3>\n\n\n\n<p>The chief responsibility of <em>Vision<\/em>&#8216;s main application is to provide the system for hosting the pluggable modules doing the actual visualization of a particular type of Omnified data. The underlying plugin system is powered <a href=\"https:\/\/github.com\/BadEcho\/core\/tree\/master\/src\/Common\/Extensibility\" target=\"_blank\" rel=\"noreferrer noopener\">Bad Echo&#8217;s Extensibility framework<\/a>, which itself uses <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/framework\/mef\/\" target=\"_blank\" rel=\"noreferrer noopener\">MEF2<\/a>.<\/p>\n\n\n\n<p>Modules are loaded during the assembly of the root <em>Vision<\/em> window&#8217;s data context. By default, the module plugin files are discovered by looking through the <code>.\\plugins<\/code> directory, whose location is relative to that of the executable for <em>Vision<\/em>. This of course, can be changed via standard configuration settings exposed by Bad Echo&#8217;s Extensibility framework.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionPluginsDirectory.png\"><img loading=\"lazy\" width=\"692\" height=\"149\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionPluginsDirectory.png\" alt=\"Here we see two comfy Vision modules, nestled inside a plugin directory.\" class=\"wp-image-2238\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionPluginsDirectory.png 692w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionPluginsDirectory-300x65.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionPluginsDirectory-480x103.png 480w\" sizes=\"(max-width: 692px) 100vw, 692px\" \/><\/a><figcaption>Here we see two comfy Vision modules, nestled inside a plugin directory.<\/figcaption><\/figure><\/div>\n\n\n\n<p>The framework for creating a <em>Vision<\/em> module plugin is provided by <a href=\"https:\/\/github.com\/BadEcho\/vision\/tree\/master\/src\/Vision.Extensibility\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Vision<\/em>&#8216;s own Extensibility library<\/a>. Modules simply reference this library and export an implementation of the <a href=\"https:\/\/github.com\/BadEcho\/vision\/blob\/master\/src\/Vision.Extensibility\/IVisionModule.cs\" target=\"_blank\" rel=\"noreferrer noopener\"><code>IVisionModule<\/code><\/a> interface and, lo and behold, they will be loaded by <em>Vision<\/em> as a plugin.<\/p>\n\n\n\n<h6>Modules are configurable<\/h6>\n\n\n\n<p>At the time of a module&#8217;s initialization, its <code>IVisionModule<\/code> export is provided with <em>Vision<\/em>&#8216;s application configuration, found in the <code>settings.json<\/code> file located alongside <em>Vision<\/em>&#8216;s executable. This configuration contains not only global application settings, but also module-specific ones.<\/p>\n\n\n\n<p>An example of a module-specific configuration setting is the anchor point location for a module. Even though a module has a default anchor point location defined in its <code>IVisionModule<\/code> export, it is the configuration provided through <em>Vision<\/em> that (assuming the location setting is present) will ultimately decide which anchor point the module ends up being attached to. <\/p>\n\n\n\n<p>Let&#8217;s look at an example configuration file:<\/p>\n\n\n\n<h6>settings.json for two modules<\/h6>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{\n  &quot;launchDisplay&quot;: 0,\n  &quot;leftAnchorMargin&quot;:  &quot;0,140,0,0&quot;,\n  &quot;titleLocation&quot;: &quot;TopLeft&quot;,\n  &quot;messageFilesDirectory&quot;: &quot;..\\\\..\\\\hacks\\\\targets\\\\nioh2&quot;,\n  &quot;modules&quot;: {\n    &quot;BadEcho.Vision.Statistics&quot;: {\n      &quot;location&quot;: &quot;TopLeft&quot;\n    },\n    &quot;BadEcho.Vision.Apocalypse&quot;: {\n      &quot;location&quot;: &quot;BottomCenter&quot;,\n      &quot;maxMessages&quot;: 3,\n      &quot;effectMessageMaxWidth&quot;: 650\n    } \n  }\n} \n<\/pre>\n\n\n<p>Module-specific configuration sections are found underneath the <code>\"modules\"<\/code> property, the specific module being denoted by the name of the assembly containing the module. In the above example we can see anchor point locations of <code>\"TopLeft\"<\/code> and <code>\"TopRight\"<\/code> configured for the Statistics and Apocalypse modules respectively. <\/p>\n\n\n\n<p>Like all Bad Echo technology products, the configuration file itself is hot-pluggable, meaning that any changes made to it will be applied immediately, without having to reload the application.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/sus_112.png\" alt=\"Omni Suspicious...\" class=\"wp-image-2263\" width=\"56\" height=\"56\"\/><\/figure><\/div>\n\n\n\n<p>So, we have an appropriately designed layout system and the means to load the plugins that will populate said layout. That&#8217;s still not enough for <em>Vision<\/em> to be useful, however, as there&#8217;s one critical component still missing: the means to provide exported Omnified game data to a loaded module for rendering.<\/p>\n\n\n\n<p>Let&#8217;s take a look at the final piece of the platform: <em>Vision<\/em>&#8216;s message file system.<\/p>\n\n\n\n<h3>Message File System<\/h3>\n\n\n\n<p>We could have the coolest looking set of <em>Vision<\/em> modules, all perfectly laid out on <em>Vision&#8217;s<\/em> overlay so that our ability to view the game&#8217;s normal UI isn&#8217;t impeded; but, without some actual exported Omnified data for these modules to render, we&#8217;ll end up with nothing but a blank overlay.<\/p>\n\n\n\n<p>In a previous section, we saw how the Omnified hacking code injected into the target binary is responsible for collecting and dumping data of interest. It&#8217;s up to <em>Vision<\/em>&#8216;s main application to load this exported data, provide it to the relevant <em>Vision<\/em> module, and then monitor said data for future updates.<\/p>\n\n\n\n<p>Given the primitive nature of the code responsible for exporting Omnified data, we&#8217;re limited to primitive means of communicating it; namely, by use of the filesystem in the form of message files.<\/p>\n\n\n\n<p>Each <em>Vision<\/em> module has its own message file, whose name is defined by the module within its exported <code>IVisionModule<\/code> implementation. <em>Vision<\/em> locates this message file, and then begins to monitor for changes, publishing said changes to the particular module interested in it.<\/p>\n\n\n\n<h6><em>Vision <\/em>is data agnostic<\/h6>\n\n\n\n<p><em>Vision<\/em>&#8216;s message file system is actually completely data agnostic; it has no knowledge or understanding of the particular format that the message file is using. It&#8217;s the modules themselves that understand the data format. All the <em>Vision<\/em> platform needs to do is to be able to read the content and pass it on to the correct place.<\/p>\n\n\n\n<p>Other than knowing where to read the content from, the only thing <em>Vision<\/em> needs to be cognizant about is the manner in which updates are being written to the message file. For some types of Omnified data, there can only exist a single instance of said data at any given point in time. For others, the data grows over time, and is more event based.<\/p>\n\n\n\n<h6>Incremental vs whole message file updates<\/h6>\n\n\n\n<p>When a message file is written to, <em>Vision<\/em> will process either the entire file, or just the content added to it since the last read. This difference in behavior is basically the only way <em>Vision<\/em> treats different message data&#8230;well, differently!<\/p>\n\n\n\n<p>Typically, any kind of data that can be construed as some sort of historical record, or a compilation of events (if you will), probably isn&#8217;t intended to have all of its past entries processed every single time it gets a byte is appended to the file.<\/p>\n\n\n\n<p><em>Vision<\/em> determines which processing behavior it&#8217;ll use by looking at a module&#8217;s <code>IVisionModule<\/code> implementation, which knows best how its own data should be fed to it. More concrete examples of this will be provided in the upcoming sections on some specific <em>Vision<\/em> modules.<\/p>\n\n\n\n<h6>We&#8217;ve covered the platform, let&#8217;s take a look at it in use<\/h6>\n\n\n\n<p>The stars of the show are the <em>Vision<\/em> modules. They&#8217;re the components that actually paint the Omnified data on the screen. <\/p>\n\n\n\n<p>And now that we&#8217;ve sufficiently covered the platform for <em>Vision<\/em>, let&#8217;s take a look at the modules that were fully implemented at the time the Omnified main experiment came to a close. These modules were successfully able to augment an Omnified game so that it looked like the following:<\/p>\n\n\n\n<div id=\"vision-full-shot\" class=\"wp-block-image fancybox\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen-1024x576.png\" alt=\"A full shot of Vision running on top of Omnified Nioh 2.\" class=\"wp-image-2282\" width=\"1024\" height=\"576\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen-1024x576.png 1024w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen-300x169.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen-768x432.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen-1536x864.png 1536w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen-480x270.png 480w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionEntireScreen.png 1920w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>A full shot of <em>Vision<\/em> running on top of Omnified <em>Nioh 2<\/em> (click to enlarge).<\/figcaption><\/figure><\/div>\n\n\n\n<h2>The Statistics Module<\/h2>\n\n\n\n<p>The first of <em>Vision&#8217;s<\/em> modules which we&#8217;ll be taking a look at, and indeed the first module made for <em>Vision<\/em>, is its <a href=\"https:\/\/github.com\/BadEcho\/vision\/tree\/master\/src\/Vision.Statistics\" target=\"_blank\" rel=\"noreferrer noopener\">Statistics module.<\/a><\/p>\n\n\n\n<p>This module is responsible for displaying, rather beautifully on screen (I&#8217;d hope), raw game statistics exported from an Omnified game. The inspiration for this stems from one of the earliest &#8220;next-level&#8221; ideas that sprung forth from my work hacking games: the need to be able to display to the viewer some of the otherwise unavailable game data on screen.<\/p>\n\n\n\n<p>Plus, Omnified games often deal with ridiculous numbers (of the kind that are bad for the player&#8217;s longevity when too big, typically), and I wanted those numbers visible.<\/p>\n\n\n\n<p>All preferably in a fashion that was both easy to read and nice to look at. This was not the case initially, as the best I could manage (I was working fast!) was a window capture of a notepad-like program that would slowly refresh with the updated contents of a dumped statistics file.<\/p>\n\n\n\n<h6>With <em>Vision<\/em>, things look a whole lot better<\/h6>\n\n\n\n<p>Referencing the image provided above, here&#8217;s where the Statistics module resides:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatistics.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatistics.png\" alt=\"A closeup of the Statistics module for Vision.\" class=\"wp-image-2283\" width=\"357\" height=\"600\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatistics.png 357w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatistics-179x300.png 179w\" sizes=\"(max-width: 357px) 100vw, 357px\" \/><\/a><figcaption>A closeup of the Statistics module for Vision.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Beautiful, floating, and easily readable game statistics text, made possible by a customized outlined text element control (part of the Bad Echo Presentation framework) and, of course, the <em>Vision<\/em> platform!<\/p>\n\n\n\n<p>Not only is it nicer looking, but it is also <em>much<\/em> more functional as well. Before <em>Vision<\/em>, the on-screen display of Omnified stats would maybe take two or more seconds to reflect updated values from the game. Not so anymore, as <em>Vision<\/em> is able to show hacked game data in near real time:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsStamina.gif\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsStamina.gif\" alt=\"The Statistics module's stamina, shown side by side with the game's own meter, accurately reflecting changes to the player's stamina.\" class=\"wp-image-2284\" width=\"698\" height=\"145\"\/><\/a><figcaption>The Statistics module&#8217;s stamina, shown side by side with the game&#8217;s own meter, accurately reflecting changes to the player&#8217;s stamina.<\/figcaption><\/figure><\/div>\n\n\n\n<h6>The custom technology required to make the above functionality possible is many and varied<\/h6>\n\n\n\n<p>I won&#8217;t be doing a deep, deep technical dive into everything that was done; the source code is available, so curious minds can find all the answers they desire by consulting that resource.<\/p>\n\n\n\n<p>But all that aside, it definitely goes without saying that the various systems that <em>were<\/em> described earlier, like the message file system and the Omnified hacking framework itself, etc., play a big role in allowing such a smooth real-time reporting experience of hacked data.<\/p>\n\n\n\n<p>There is a framework and design adhered to by the Statistics module itself as well, however &#8212; we&#8217;re not dealing with simple &#8220;name and value&#8221; pairs here. So, with that being said, let&#8217;s pull back the curtain a bit and take a look at what a &#8220;statistic&#8221; actually is.<\/p>\n\n\n\n<h3>Types of Statistics<\/h3>\n\n\n\n<p>As you may know, the Omnified process employs systems that are able to reshape gameplay experiences using totally game-neutral code. It should come as no surprise then, that <em>Vision<\/em> and its modules were also designed to be game-neutral.<\/p>\n\n\n\n<p>That means, if a game is <a href=\"https:\/\/badecho.com\/index.php\/what-is-omnified\/\" target=\"_blank\" rel=\"noreferrer noopener\">Omnified<\/a>, it will completely work with <em>Vision<\/em> without requiring any kind of changes or updates to <em>Vision<\/em> at all. The (game-neutral) statistics provided by <em>Vision<\/em> for Omnified <em>Nioh 2<\/em> would immediately be present and visualized upon the Omnification of any future game.<\/p>\n\n\n\n<h6>Game-neutral systems automatically provide game-neutral statistics<\/h6>\n\n\n\n<p>And, it&#8217;s also because the statistics themselves follow a design. This is observable by consulting the <a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/framework\/statisticMessages.lua\" target=\"_blank\" rel=\"noreferrer noopener\">Omnified game statistic messaging schema<\/a> in the hacking framework, and it is adhered to by the Statistics <em>Vision<\/em> module&#8217;s object model.<\/p>\n\n\n\n<p>Let&#8217;s go through the different types of statistics now.<\/p>\n\n\n\n<h4>Whole Statistics<\/h4>\n\n\n\n<p>These are statistics that each concern a whole, numeric value. <\/p>\n\n\n\n<p>The term &#8220;whole&#8221; is not in reference to the actual data type of the statistic, which may either be an integer or a non-integer. Rather, a statistic is considered &#8220;whole&#8221; if it is expressed using a single numeric value.<\/p>\n\n\n\n<p>For example, the character&#8217;s experience level (e.g., a level <strong>20<\/strong> mage) and the number of times the player has died during the playthrough (e.g., <strong>1230<\/strong> times) are both examples of a whole statistic.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionWholeStatistics-e1644721677303.png\"><img loading=\"lazy\" width=\"260\" height=\"87\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionWholeStatistics-e1644721677303.png\" alt=\"These three statistics are all considered to be &quot;whole statistics&quot;, even though two of them can be fractional in value (a percentage value of 100% is 1, 50% is 0.5, etc.).\" class=\"wp-image-2308\"\/><\/a><figcaption>Three statistics that are all considered to be &#8220;whole statistics&#8221;.<\/figcaption><\/figure><\/div>\n\n\n\n<h4>Fractional Statistics<\/h4>\n\n\n\n<p>These are statistics that each concern a fractional, numeric value.<\/p>\n\n\n\n<p>Like the term &#8220;whole&#8221;, the term &#8220;fractional&#8221; has no bearing on the data type being used. Rather, a statistic is considered &#8220;fractional&#8221; if it is expressed using values that have some sort of relationship to each other.<\/p>\n\n\n\n<p>For example, the player&#8217;s health and stamina, as displayed in the above images, are fractional statistics. This is because each one consists of a current value, and a maximum value. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsFractional-e1644721844843.png\"><img loading=\"lazy\" width=\"332\" height=\"59\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsFractional-e1644721844843.png\" alt=\"These two statistics are considered to be &quot;fractional statistics&quot;, as each one can only be expressed by using both the current and maximum values.\" class=\"wp-image-2309\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsFractional-e1644721844843.png 332w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsFractional-e1644721844843-300x53.png 300w\" sizes=\"(max-width: 332px) 100vw, 332px\" \/><\/a><figcaption>These two statistics are considered to be &#8220;fractional statistics&#8221;.<\/figcaption><\/figure><\/div>\n\n\n\n<p>The distinction between fractional and whole statistics affords us greater compactness as well as additional capabilities, such as the ability to present the statistics as a bar or gauge, <a href=\"https:\/\/badecho.com\/index.php\/2021\/10\/11\/smooth-health-bars\/\" target=\"_blank\" rel=\"noreferrer noopener\">as I detailed in a previous article<\/a>.<\/p>\n\n\n\n<h4>Coordinate Statistics<\/h4>\n\n\n\n<p>These are statistics that each concern a coordinate triplet value.<\/p>\n\n\n\n<p>One of the few requirements of an Omnified game is that it operates in three-dimensional space. As this requirement is satisfied by most games, it has never been an issue.<\/p>\n\n\n\n<p>The players coordinates are typically one of the first things reversed engineered when I&#8217;m Omnifying a game; given that these values aren&#8217;t normally exposed to the player, I would&#8217;ve hoped that their display on screen was of interest for most viewers and lovers of games.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsCoordinates.png\"><img loading=\"lazy\" width=\"357\" height=\"86\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsCoordinates.png\" alt=\"This statistic is considered to be a &quot;coordinate statistic&quot;, since it is expressed through the use of a coordinate triplet value.\" class=\"wp-image-2310\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsCoordinates.png 357w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsCoordinates-300x72.png 300w\" sizes=\"(max-width: 357px) 100vw, 357px\" \/><\/a><figcaption>This statistic is considered to be a &#8220;coordinate statistic&#8221;.<\/figcaption><\/figure><\/div>\n\n\n\n<p>We could have expressed the player&#8217;s coordinates using a statistic of the type we&#8217;ll be talking about next; however, having a statistic type made specifically for entity coordinates affords us some additional, specialized layout opportunities, among other things.<\/p>\n\n\n\n<h4>Statistic Groups<\/h4>\n\n\n\n<p>The final type of statistic is known as a &#8220;statistic group&#8221;. While it is indeed a statistic itself, its distinction among the other statistic types is that instead of containing a simple value, it contains other statistics.<\/p>\n\n\n\n<p>These other statistics can be of any type: whole, fractional, coordinate, or even (I suppose) other statistic groups! The statistic group type exists for purposes of saving screen real estate, best understood by taking a look some example statistic groups.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticGroups.png\"><img loading=\"lazy\" width=\"260\" height=\"160\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticGroups.png\" alt=\"Two &quot;statistic groups&quot; are pictured here containing three and four &quot;whole statistics&quot;, respectively.\" class=\"wp-image-2315\"\/><\/a><figcaption>Two &#8220;statistic groups&#8221; are pictured here.<\/figcaption><\/figure><\/div>\n\n\n\n<p>There are two statistic groups shown above, one for statistics related to damage the player has received from enemies, and one for statistics related to damage the player has inflicted on enemies.<\/p>\n\n\n\n<p>Each child statistic belonging to these groups are themselves whole statistics. Now, instead of having multiple statistics with wastefully long names like &#8220;Max Damage Taken&#8221;, we can get away with simply &#8220;Max&#8221;.<\/p>\n\n\n\n<h3>Module Operation<\/h3>\n\n\n\n<p>The Statistics module for <em>Vision<\/em> displays the data being written to its particular message file, which by default will be found in the <code>target<\/code> directory of the Omnified game, with the name <code>statistics.json<\/code>.<\/p>\n\n\n\n<p>As one can glean from looking at the message file&#8217;s name, Omnified statistics are encoded in JSON. A snapshot of the complete set of statistics is committed to the message file, meaning that the entire file is processed by <em>Vision<\/em> every time a write has occurred to it.<\/p>\n\n\n\n<h6>Example statistics.json dump<\/h6>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n[\n    {\n        &quot;Type&quot;: 1,\n        &quot;Statistic&quot;: {\n            &quot;SecondaryBarColor&quot;: &quot;#AA27D88D&quot;,\n            &quot;CurrentValue&quot;: 2714,\n            &quot;MaximumValue&quot;: 2714,\n            &quot;Name&quot;: &quot;Health&quot;,\n            &quot;PrimaryBarColor&quot;: &quot;#AA43BC50&quot;\n        }\n    },\n    {\n        &quot;Type&quot;: 1,\n        &quot;Statistic&quot;: {\n            &quot;SecondaryBarColor&quot;: &quot;#AAB22DE5&quot;,\n            &quot;CurrentValue&quot;: 1373,\n            &quot;MaximumValue&quot;: 1373,\n            &quot;Name&quot;: &quot;Stamina&quot;,\n            &quot;PrimaryBarColor&quot;: &quot;#AA7515D9&quot;\n        }\n    },\n    {\n        &quot;Type&quot;: 0,\n        &quot;Statistic&quot;: {\n            &quot;Value&quot;: 0,\n            &quot;Name&quot;: &quot;Amrita (XP)&quot;\n        }\n    },\n    {\n        &quot;Type&quot;: 0,\n        &quot;Statistic&quot;: {\n            &quot;Value&quot;: 0,\n            &quot;Name&quot;: &quot;Enemy Health&quot;\n        }\n    },\n    {\n        &quot;Type&quot;: 3,\n        &quot;Statistic&quot;: {\n            &quot;Name&quot;: &quot;Damage Taken&quot;,\n            &quot;Statistics&quot;: [\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;Name&quot;: &quot;Last&quot;\n                    }\n                },\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;IsCritical&quot;: true,\n                        &quot;Name&quot;: &quot;Max&quot;\n                    }\n                },\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;Name&quot;: &quot;Total&quot;\n                    }\n                }\n            ]\n        }\n    },\n    {\n        &quot;Type&quot;: 3,\n        &quot;Statistic&quot;: {\n            &quot;Name&quot;: &quot;Damage Inflicted&quot;,\n            &quot;Statistics&quot;: [\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;Name&quot;: &quot;Hits&quot;\n                    }\n                },\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;Name&quot;: &quot;Last&quot;\n                    }\n                },\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;IsCritical&quot;: true,\n                        &quot;Name&quot;: &quot;Max&quot;\n                    }\n                },\n                {\n                    &quot;Type&quot;: 0,\n                    &quot;Statistic&quot;: {\n                        &quot;Value&quot;: 0,\n                        &quot;Name&quot;: &quot;Total&quot;\n                    }\n                }\n            ]\n        }\n    },\n    {\n        &quot;Type&quot;: 2,\n        &quot;Statistic&quot;: {\n            &quot;Y&quot;: 2558.3046875,\n            &quot;Format&quot;: &quot;{0:0.000}&quot;,\n            &quot;Z&quot;: 14355.823242188,\n            &quot;Name&quot;: &quot;Coordinates&quot;,\n            &quot;X&quot;: 18031.18359375\n        }\n    },\n    {\n        &quot;Type&quot;: 0,\n        &quot;Statistic&quot;: {\n            &quot;Value&quot;: 100,\n            &quot;IsCritical&quot;: false,\n            &quot;Name&quot;: &quot;Player Damage&quot;,\n            &quot;Format&quot;: &quot;{0}%&quot;\n        }\n    },\n    {\n        &quot;Type&quot;: 0,\n        &quot;Statistic&quot;: {\n            &quot;Value&quot;: 100,\n            &quot;IsCritical&quot;: false,\n            &quot;Name&quot;: &quot;Player Speed&quot;,\n            &quot;Format&quot;: &quot;{0}%&quot;\n        }\n    },\n    {\n        &quot;Type&quot;: 0,\n        &quot;Statistic&quot;: {\n            &quot;Value&quot;: 3244,\n            &quot;Name&quot;: &quot;Deaths&quot;\n        }\n    }\n]\n<\/pre>\n\n\n<p>Of course, optimizations are in place to ensure that UI controls aren&#8217;t redrawn unless they actually need to be (i.e., the value must change).<\/p>\n\n\n\n<h6>Most statistics are per-session<\/h6>\n\n\n\n<p>When a target binary is injected with an Omnified hack, most of the statistics are initialized to default values in memory. Only one of the stock statistics has its value maintained between sessions, and that&#8217;s the statistic responsible for tracking the number of deaths.<\/p>\n\n\n\n<p>This is achieved through special logic in the Omnified messaging library, as well as a supplementary file designed to hold the death count, as the message file itself is a volatile place. <\/p>\n\n\n\n<p>The whole process could probably be improved (removing the dependency on a separate file for starters) and made generalized so other statistics could be maintained between sessions; however, as the Omnified experiment has concluded (for now), this probably won&#8217;t happen anytime soon.<\/p>\n\n\n\n<h6>Grabbing attention with explosions<\/h6>\n\n\n\n<p>For the most part, a viewer can expect changes in statistics to simply be reflected by the Statistics module as those changes happen; rather immediately as well, as was made clear by the previously provided picture showing <em>Vision<\/em>&#8216;s stamina bar next to the game&#8217;s own.<\/p>\n\n\n\n<p>Some changes are more important than others, however. An example being that of the maximum damage received by the player. Omnified games greatly amplify the player&#8217;s incoming damage, and a point of pride is definitely how much damage I&#8217;m having to tolerate while playing a game I&#8217;ve Omnified.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsMaxDamage.gif\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionStatisticsMaxDamage.gif\" alt=\"Normal operation of the Statistics module: in this case, an explosive update being made to the maximum damage received stat after the player gets walloped by the enemy.\" class=\"wp-image-2285\" width=\"723\" height=\"487\"\/><\/a><figcaption>Normal operation of the Statistics module: in this case, an explosive update being made to the maximum damage received stat.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Hitting a new &#8220;highest damage received&#8221; number is like hitting the jackpot in an Omnified playthrough! The effect shown above is achieved through a special little animation I have in the Statistics module, lovingly termed <code>ExplodeTextAnimation<\/code>. It&#8217;s a neat little effect that makes the update to the statistic hard to miss.<\/p>\n\n\n\n<p>This animation plays out whenever a new, higher value is written to a <em>critical<\/em> whole statistic. A whole statistic is considered critical if the <code>IsCritical<\/code> property of the statistic is set to <code>true<\/code>. Only a few of the stock statistics are marked as critical; if one does desire more exploding text, you&#8217;ll be happy to know it&#8217;s not hard to add your own.<\/p>\n\n\n\n<h6>Custom statistics are easy to add<\/h6>\n\n\n\n<p>Simply by Omnifying a game, all of the standard, game-neutral statistics will be wired up and able to be displayed by <em>Vision<\/em>. While that&#8217;s lovely, often there are going to be statistics completely specific to the Omnified game which we&#8217;ll want to show.<\/p>\n\n\n\n<p>For example, if I&#8217;ve Omnified a shooter, it may become desirable to display the number of bullets in a currently equipped gun&#8217;s magazine (whose normal representation on the game&#8217;s HUD is most likely being blocked by my webcam&#8217;s image if I&#8217;m <a href=\"https:\/\/twitch.tv\/omni\" target=\"_blank\" rel=\"noreferrer noopener\">streaming the game<\/a>).<\/p>\n\n\n\n<p>Luckily, adding a custom, game-specific stat requires no modifications to be made to either the Omnified hacking framework or <em>Vision<\/em> itself. All one needs to do is <a href=\"https:\/\/github.com\/BadEcho\/omnified-hacks\/blob\/master\/targets\/nioh2\/exports.lua\" target=\"_blank\" rel=\"noreferrer noopener\">create an <code>exports.lua<\/code> file in the Omnified game&#8217;s <code>target<\/code> directory<\/a>, and have it contain a <code>registerExports<\/code> method that initializes the <code>additionalStatistics<\/code> variable appropriately.<\/p>\n\n\n\n<h6>Example exports.lua<\/h6>\n\n\n<pre class=\"brush: lua; title: ; notranslate\" title=\"\">\nrequire(&quot;statisticMessages&quot;)\n\nfunction registerExports()\n    -- Custom statistics.  \n    additionalStatistics = function()\n        local bulletCount = toInt(readInteger(&quot;bulletCount&quot;))\n\n        return { \n            WholeStatistic(&quot;Bullet Count&quot;, bulletCount)\n        }\n    end\nend\n\nfunction unregisterExports()\n\nend\n<\/pre>\n\n\n<p>This, of course, assumes that the appropriate data collection code has been written somewhere in the target binary&#8217;s <code>hooks.asm<\/code>, properly dumping live values of the bullet count to the address of <code>bulletCount<\/code>. My game-neutral code is general purpose, not omniscient.<\/p>\n\n\n\n<p>That aside, it&#8217;s quite easy to add as many custom statistics as we please to an Omnified offering.<\/p>\n\n\n\n<h6>The interface can be hidden if it gets in the way<\/h6>\n\n\n\n<p>With potentially all of these custom statistics in addition to the stock statistics, there may be certain screens in the game containing elements that <em>Vision<\/em> ends up obstructing. A proper anchor point location only gets us so far.<\/p>\n\n\n\n<p>Luckily (and this applies to the entirety of <em>Vision<\/em>, not just the Statistics module), the overlay can be hidden and revealed at a later point at the press of a hotkey.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionHideShow.gif\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionHideShow.gif\" alt=\"If Vision actually does get in the way, a simple button press gets it out of sight, out of mind -- until we want it back.\" class=\"wp-image-2288\" width=\"518\" height=\"636\"\/><\/a><figcaption>If Vision actually does get in the way, a simple button press gets it out of sight, out of mind &#8212; until we want it back.<\/figcaption><\/figure><\/div>\n\n\n\n<h6>And that&#8217;s the first of the modules we&#8217;ll be discussing<\/h6>\n\n\n\n<p>I&#8217;m quite proud of the Statistics module, and there&#8217;s much more that could be said about it. If I ever find myself returning to do further Omnified development, there&#8217;s much more I will be able to do with it.<\/p>\n\n\n\n<p>Whether or not that happens, I&#8217;m pleased with it in its current state. To see more of it in action, you can check out my <a href=\"https:\/\/twitch.tv\/omni\" target=\"_blank\" rel=\"noreferrer noopener\">stream&#8217;s VODs<\/a>.<\/p>\n\n\n\n<p>But, before you do that, let&#8217;s discuss the second module of <em>Vision<\/em>, whose completion essentially marked the wrapping up of the Omnified endeavor.<\/p>\n\n\n\n<h2>The Apocalypse Module<\/h2>\n\n\n\n<p>When I decided to refocus my efforts from <a href=\"https:\/\/twitch.tv\/omni\" target=\"_blank\" rel=\"noreferrer noopener\">livestreaming hacked-to-insanity games<\/a> to developing my own game, I did so with a condition in place requiring me to complete the Apocalypse module for <em>Vision<\/em> first. This condition has since then been satisfied.<\/p>\n\n\n\n<p>Arguably more invaluable than even the Statistics module in helping a casual observer understand the Omnified experience: the <a href=\"https:\/\/github.com\/BadEcho\/vision\/tree\/master\/src\/Vision.Apocalypse\" target=\"_blank\" rel=\"noreferrer noopener\">Apocalypse module<\/a> grants insight into the mysteries of the <a href=\"https:\/\/badecho.com\/index.php\/2020\/10\/19\/apocalypse-system\/\" target=\"_blank\" rel=\"noreferrer noopener\">Apocalypse system<\/a> itself by providing a display of all the Apocalypse-related events occurring in the game.<\/p>\n\n\n\n<h6>Check out <a href=\"https:\/\/badecho.com\/index.php\/2020\/10\/19\/apocalypse-system\/\" target=\"_blank\" rel=\"noreferrer noopener\">the dedicated article<\/a> for information on the Apocalypse system<\/h6>\n\n\n\n<p>A complete overview of the Apocalypse system is not possible here, and I&#8217;d recommend checking out the linked article dedicated to it. It&#8217;s more or less up to date, and I&#8217;ll probably do one last revision of it in the future for the sake of accuracy, if needed.<\/p>\n\n\n\n<p>To summarize: the Apocalypse is one of my Omnified game-neutral systems, with the very specific purpose of overhauling a game&#8217;s built-in damage system. Instead of just receiving damage, the Apocalypse system will &#8220;roll some dice&#8221; and apply a random effect based on that damage.<\/p>\n\n\n\n<p>This typically results in many, horrific deaths for the player. That&#8217;s a rather significant augmentation of an important game system. It&#8217;s something that can easily turn a love tap into a deathblow.<\/p>\n\n\n\n<h6>Making a big change a little less confusing<\/h6>\n\n\n\n<p>With such a drastic change to gameplay, it becomes critical that we be able to see <em>why<\/em> exactly a <strong>normally light<\/strong> amount of damage happened to just <strong>kill us<\/strong>. Luckily for you and me, the Apocalypse module of <em>Vision<\/em> does just that, and it does it by elegantly displaying all of the various &#8220;decisions&#8221; the Apocalypse system is making behind the scenes.<\/p>\n\n\n\n<p>Similar to how Omnified statistics used to be displayed to viewers, Apocalypse events were previously shown using a primitive and ugly window capture; although, in Apocalypse&#8217;s case, the window being captured was a console window that was basically running a <code>tail<\/code> command on a log file.<\/p>\n\n\n\n<p>The window and its text had to be quite small, as the transparency of the window could only be increased so much before the text was rendered invisible. That means there <em>had<\/em> to be a semi-transparent black box with text on the bottom or top of the game screen. A veritable eye sore.<\/p>\n\n\n\n<h6>Fortunately, with <em>Vision<\/em>, things once again look a whole lot better<\/h6>\n\n\n\n<p>Referencing the original <a href=\"#vision-full-shot\">full shot of <em>Vision<\/em> provided above<\/a>, here&#8217;s where the Apocalypse module resides:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypse-e1644706022651.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypse-e1644706022651.png\" alt=\"A closeup of the Apocalypse module for Vision.\" class=\"wp-image-2286\" width=\"961\" height=\"211\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypse-e1644706022651.png 961w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypse-e1644706022651-300x66.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypse-e1644706022651-768x169.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypse-e1644706022651-480x105.png 480w\" sizes=\"(max-width: 961px) 100vw, 961px\" \/><\/a><figcaption>A closeup of the Apocalypse module for Vision.<\/figcaption><\/figure><\/div>\n\n\n\n<p>There&#8217;s a lot of important information to communicate when discussing an Apocalypse event, and I think the Apocalypse module, as evidenced by the above image, does it rather well. With a proper anchor point location designated, it can do its difficult job while still allowing us to see the game we&#8217;re playing.<\/p>\n\n\n\n<h3>Visualizing Apocalypse Events<\/h3>\n\n\n\n<p>I always envisioned the Apocalypse system in the back of my mind as an evil Dungeon Master rolling a many-sided die in order to determine our fates. Indeed, there are essentially numerous die rolls occurring within the Apocalypse assembly code, and it&#8217;s these rolls that the old display logic would, rather verbosely, report on.<\/p>\n\n\n\n<p>With <em>Vision,<\/em> we can elect to describe these rolls using imagery as opposed to words. This was achieved by implementing each type of die as a WPF view containing declarative <code><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.windows.media.drawingimage?view=windowsdesktop-6.0\" target=\"_blank\" rel=\"noreferrer noopener\">DrawingImage<\/a><\/code> XAML code (converted from an SVG format) that described the die&#8217;s shape.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/DieRoll.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/DieRoll.png\" alt=\"The die view used for primary Apocalypse rolls, as seen in the XAML designer, bound to an event with a die roll of 5.\" class=\"wp-image-2321\" width=\"253\" height=\"253\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/DieRoll.png 253w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/DieRoll-150x150.png 150w\" sizes=\"(max-width: 253px) 100vw, 253px\" \/><\/a><figcaption>The die view used for primary Apocalypse rolls.<\/figcaption><\/figure><\/div>\n\n\n\n<p>These die UI controls are essentially vector graphics, so the drawings are scaled to be as large as the space I allow for them; the ones being pictured here having been given much more room than what they&#8217;d normally get when hosted by an Apocalypse event view.<\/p>\n\n\n\n<p>The majority of Apocalypse events, particularly the ones that target the player, feature a primary die roll. The results of the roll are displayed by the Apocalypse <em>Vision<\/em> module using the die view pictured above.<\/p>\n\n\n\n<p>These aren&#8217;t the only kinds of rolls the Apocalypse system engages in, however. Should the primary die roll yield a 7, 8, or 9, then a supplementary <em>risk of murder<\/em> roll is triggered. If that roll ends up on the wrong number: <em>we&#8217;re dead<\/em>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/RiskOfMurderDieRoll.png\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/RiskOfMurderDieRoll.png\" alt=\"The die view used for &quot;risk of murder&quot; Apocalypse rolls, as seen in the XAML designer, bound to an event with a die roll of 5. This die will kill ya!\" class=\"wp-image-2323\" width=\"253\" height=\"253\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/RiskOfMurderDieRoll.png 253w, https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/RiskOfMurderDieRoll-150x150.png 150w\" sizes=\"(max-width: 253px) 100vw, 253px\" \/><\/a><figcaption>The die view used for &#8220;risk of murder&#8221; Apocalypse rolls.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Spiky and deadly! Now, instead of a bunch of words talking about all these rolls we can just display the dice themselves on the screen with just one or two (in the case of a murder roll) columns worth of space being occupied.<\/p>\n\n\n\n<h6>Effect text, and effect sound<\/h6>\n\n\n\n<p>Other than the die visuals, <em>Vision<\/em> also lets me slap a bunch of nicely outlined and laid out text on the screen. That&#8217;s really our main prerogative: textually describing the enforcement of an Omnified rule. <\/p>\n\n\n\n<p>We need to do a little more than that, however. Sometimes, an event&#8217;s appearance is also accompanied by a lovely and rather jarring sound, as anyone who has watched one of my broadcasts would know (hint, hint: think <em>Duke Nukem<\/em>, Tom Petty, etc.).<\/p>\n\n\n\n<p>Previously, the Omnified hacking framework itself took care of playing the sound effects, which made sense as it also used to contain the actual (primitive) display logic for the events. With the move to <em>Vision<\/em>, the Apocalypse module takes care of all the sound effect randomization and playback instead.<\/p>\n\n\n\n<h3>Module Operation<\/h3>\n\n\n\n<p>The Apocalypse module for <em>Vision <\/em>displays the events being logged to its particular message file, which by default will be found in the <code>target<\/code> directory of the Omnified game, with the name <code>apocalypse.jsonl<\/code>.<\/p>\n\n\n\n<p>This message file and its format are a bit different from what the Statistics module uses &#8212; and its format might be one you haven&#8217;t encountered before: <a href=\"https:\/\/jsonlines.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">JSON Lines<\/a>. This format allows us to store structured, JSON-encoded data that can be parsed one record at a time as each record gets added to the message file.<\/p>\n\n\n\n<h6>Example apocalypse.jsonl dump<\/h6>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{&quot;Event&quot;:{&quot;Damage&quot;:551,&quot;HealthAfter&quot;:1669,&quot;ZDisplacement&quot;:339.30004882813,&quot;IsFreeFalling&quot;:true,&quot;DieRoll&quot;:6,&quot;YDisplacement&quot;:1427.7600097656,&quot;Timestamp&quot;:&quot;2022-01-08T03:42:10Z&quot;,&quot;XDisplacement&quot;:718.55993652344},&quot;Type&quot;:2}\n{&quot;Event&quot;:{&quot;DieRoll&quot;:1,&quot;ExtraDamageMultiplier&quot;:2.0,&quot;Damage&quot;:872,&quot;Timestamp&quot;:&quot;2022-01-09T06:08:50Z&quot;,&quot;HealthAfter&quot;:1827},&quot;Type&quot;:1}\n{&quot;Event&quot;:{&quot;DieRoll&quot;:10,&quot;HealthHealed&quot;:872,&quot;Damage&quot;:872,&quot;Timestamp&quot;:&quot;2022-01-09T06:08:52Z&quot;,&quot;HealthAfter&quot;:2699},&quot;Type&quot;:5}\n{&quot;Event&quot;:{&quot;DieRoll&quot;:1,&quot;ExtraDamageMultiplier&quot;:2.0,&quot;Damage&quot;:1830,&quot;Timestamp&quot;:&quot;2022-01-09T06:08:52Z&quot;,&quot;HealthAfter&quot;:869},&quot;Type&quot;:1}\n{&quot;Event&quot;:{&quot;AdditionalDamage&quot;:67,&quot;BonusDamageType&quot;:0,&quot;IsExtreme&quot;:false,&quot;Timestamp&quot;:&quot;2022-01-09T06:09:52Z&quot;,&quot;BonusMultiplier&quot;:3.3},&quot;Type&quot;:0}\n{&quot;Event&quot;:{&quot;DieRoll&quot;:8,&quot;FatalisAfflicted&quot;:false,&quot;Timestamp&quot;:&quot;2022-01-09T06:10:53Z&quot;,&quot;Damage&quot;:800,&quot;MurderRoll&quot;:1,&quot;HealthAfter&quot;:69},&quot;Type&quot;:3}\n{&quot;Event&quot;:{&quot;Damage&quot;:76521,&quot;HealthAfter&quot;:0,&quot;MurderRoll&quot;:5,&quot;DieRoll&quot;:8,&quot;Timestamp&quot;:&quot;2022-01-09T06:11:53Z&quot;,&quot;MurderMultiplier&quot;:69.0},&quot;Type&quot;:4}\n<\/pre>\n\n\n<p>One can safely assume that the example provided above, due to the low number of events present, is either only a partial dump, or one for a brand-new game.<\/p>\n\n\n\n<h6>Apocalypse messages are processed incrementally<\/h6>\n\n\n\n<p>The Apocalypse module uses the alternative mode made available for processing message files: when its message file is written to, only the content that was just added to it since the last read is processed.<\/p>\n\n\n\n<p>This is because we really only care about events that are currently happening. The message file for Statistics will always contain the same number of statistics (barring us just happening to add a new, custom stat midsession), whereas the Apocalypse message file is going to keep on growing and growing.<\/p>\n\n\n\n<p>The events tell a story, and we need only worry about the most recent of developments.<\/p>\n\n\n\n<h6>Explaining murder<\/h6>\n\n\n\n<p>Of all the game-neutral Omnified systems, the Apocalypse system requires the most transparency, as it has a direct impact on one of the more crucial aspects of gameplay: whether you&#8217;re alive or dead.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypseMurdered.gif\"><img loading=\"lazy\" width=\"854\" height=\"600\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypseMurdered.gif\" alt=\"Normal operation of the Apocalypse module: in this case, the player rolling bad on a &quot;risk of murder&quot; roll and subsequently being...murdered!\" class=\"wp-image-2287\"\/><\/a><figcaption>Normal operation of the Apocalypse module: in this case, the player rolling bad on a &#8220;risk of murder&#8221; roll and subsequently being&#8230;murdered!<\/figcaption><\/figure><\/div>\n\n\n\n<p>When a new Apocalypse event is detected, its visualized form bounces down from the heavens onto your existing events (or up underneath them, depending on whether the module is anchored to the top or bottom of the screen), it then proceeds to shimmer a bit to grab attention, and then &#8220;melts&#8221; into the game.<\/p>\n\n\n\n<h6>All events are supported<\/h6>\n\n\n\n<p>The Apocalypse system has quite a few events that cause a variety of effects: increased damage to the player or enemy, restoration of the player&#8217;s health, a sudden shift in the player&#8217;s location, and even timed <a href=\"https:\/\/badecho.com\/index.php\/2021\/03\/01\/apocalypse-system-adding-fatalis-debuff\/\" target=\"_blank\" rel=\"noreferrer noopener\">debuff afflictions of doom like Fatalis<\/a>.<\/p>\n\n\n\n<p>With the Apocalypse module for <em>Vision, w<\/em>e have nice, concise visualizations for all of these events.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a class=\"fancybox\" href=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypseSampleEvents.gif\"><img loading=\"lazy\" width=\"962\" height=\"185\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/VisionApocalypseSampleEvents.gif\" alt=\"There are quite a few different events that can occur with the Apocalypse system; its Vision module tries to communicate these events to us in the best way possible.\" class=\"wp-image-2312\"\/><\/a><figcaption>There are quite a few different events that can occur with the Apocalypse system; its Vision module tries to communicate these events to us in the best way possible.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Not all possible Apocalypse events are shown above, but a number of them are. You get the picture.<\/p>\n\n\n\n<h6>Configurable screen clutter<\/h6>\n\n\n\n<p>Like the Statistics module, there&#8217;s a lot of room for configuration as far as the Apocalypse module&#8217;s behavior goes. I haven&#8217;t been able to fully exploit all of its potential, as I considered it to be a &#8220;finished product&#8221; at the moment, but there&#8217;s a lot here that can be taken advantage of.<\/p>\n\n\n\n<p>The maximum number of events displayed on the screen is essentially controlled by the <code>maxMessages<\/code> setting for the module in <em>Vision&#8217;<\/em>s <code>settings.json<\/code> configuration.<\/p>\n\n\n\n<p>In addition to that, the underlying collection view engine (provided by the Bad Echo Presentation framework), allows for delayed capacity enforcements, meaning we can allow for the events to pile up a little bit before we trim them down. This is useful for games where lots of events might occur at once.<\/p>\n\n\n\n<p>Obviously, such a mechanism might result in a complete screen takeover if a <em>ton<\/em> of events are happening. That won&#8217;t happen here, however, as there is a limit on the number of items allowed to exceed the collection view&#8217;s capacity before an immediate capacity enforcement is triggered.<\/p>\n\n\n\n<p>By default, the capacity enforcement delay limit is set to twice the value that <code>maxMessages<\/code> is set to. At the time of writing, there is no configurable endpoint to set the capacity enforcement delay itself &#8212; both of these settings most certainly deserve to be exposed in configuration, however I never got around to it.<\/p>\n\n\n\n<h6>Once again, for more information on the Apocalypse system, <a href=\"https:\/\/badecho.com\/index.php\/2020\/10\/19\/apocalypse-system\/\" target=\"_blank\" rel=\"noreferrer noopener\">check out its article<\/a><\/h6>\n\n\n\n<p>There&#8217;s lots to talk about concerning the Apocalypse system, but for the most part, all information regarding it can be found in its dedicated article linked above.<\/p>\n\n\n\n<p>Finishing the Apocalypse module for <em>Vision<\/em> was a goal of mine, and I&#8217;m proud of the result. It is the best (within reason of course) visual representation I can think of for this insane game hacking system that I created, and it&#8217;s a perfect sendoff to the Omnified experiment.<\/p>\n\n\n\n<h2>The End?<\/h2>\n\n\n\n<p>Writing this very article was the last of the self-imposed conditions that I needed to satisfy before I could &#8220;move on&#8221;.<\/p>\n\n\n\n<p>The Omnified endeavor was one of the craziest and most difficult challenges I have ever undertook, and with <em>Vision<\/em> as its culmination (along with everything developed in between), I kicked its butt.<\/p>\n\n\n\n<p>I&#8217;d love to keep doing it, however it&#8217;s a lot of work and it <a href=\"https:\/\/badecho.com\/index.php\/2021\/11\/12\/goodbye-omnified\/\" target=\"_blank\" rel=\"noreferrer noopener\">didn&#8217;t really pan out in front of a general viewing audience<\/a>.<\/p>\n\n\n\n<p>With this writeup on <em>Vision<\/em> complete, it&#8217;s time to move on to new things! I&#8217;ll be developing a game with my wife, and lots of fun technologies along the way.<\/p>\n\n\n\n<p>Of course, since Omnified gaming and <em>Vision<\/em> are my babies, I may choose to continue to work on them whenever I wish to. And probably will every now and then.<\/p>\n\n\n\n<p>Hope everyone had a fun ride; as for myself, I&#8217;m looking forward to hitting up the next one.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Vision is a game overlay platform that provides visualized data for an Omnified game; moreover, it is the culmination of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10,16],"tags":[42,28,69,64],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\r\n<title>Vision: An Omnified Game Overlay Platform - omni&#039;s hackpad<\/title>\r\n<meta name=\"description\" content=\"Vision is a provider of visualized data for an Omnified game, and the culmination of the Omnified experiment.\" \/>\r\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\r\n<link rel=\"canonical\" href=\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/\" \/>\r\n<meta property=\"og:locale\" content=\"en_US\" \/>\r\n<meta property=\"og:type\" content=\"article\" \/>\r\n<meta property=\"og:title\" content=\"Vision: An Omnified Game Overlay Platform - omni&#039;s hackpad\" \/>\r\n<meta property=\"og:description\" content=\"Vision is a provider of visualized data for an Omnified game, and the culmination of the Omnified experiment.\" \/>\r\n<meta property=\"og:url\" content=\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/\" \/>\r\n<meta property=\"og:site_name\" content=\"omni&#039;s hackpad\" \/>\r\n<meta property=\"article:published_time\" content=\"2022-02-15T03:29:28+00:00\" \/>\r\n<meta property=\"article:modified_time\" content=\"2023-03-15T06:00:49+00:00\" \/>\r\n<meta property=\"og:image\" content=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview.png\" \/>\r\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\r\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/badecho.com\/#website\",\"url\":\"https:\/\/badecho.com\/\",\"name\":\"omni&#039;s hackpad\",\"description\":\"Game Code Disassembly. Omnified Modification. Madness.\",\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/badecho.com\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/01\/VisionOverview.png\",\"width\":856,\"height\":456,\"caption\":\"Vision: An Omnified Game Overlay Platform\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#webpage\",\"url\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/\",\"name\":\"Vision: An Omnified Game Overlay Platform - omni&#039;s hackpad\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#primaryimage\"},\"datePublished\":\"2022-02-15T03:29:28+00:00\",\"dateModified\":\"2023-03-15T06:00:49+00:00\",\"description\":\"Vision is a provider of visualized data for an Omnified game, and the culmination of the Omnified experiment.\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/\"]}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#webpage\"},\"author\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"headline\":\"Vision: An Omnified Game Overlay Platform\",\"datePublished\":\"2022-02-15T03:29:28+00:00\",\"dateModified\":\"2023-03-15T06:00:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#webpage\"},\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"image\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#primaryimage\"},\"keywords\":\"C#,Omnified,Vision,WPF\",\"articleSection\":\"General Dev,Omnified Design\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/badecho.com\/index.php\/2022\/02\/14\/vision\/#respond\"]}]},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\",\"name\":\"Matt Weber\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7e345ac2708b3a41c7bd70a4a0440d41?s=96&d=mm&r=g\",\"caption\":\"Matt Weber\"},\"logo\":{\"@id\":\"https:\/\/badecho.com\/#personlogo\"}}]}<\/script>\r\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2196"}],"collection":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/comments?post=2196"}],"version-history":[{"count":91,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2196\/revisions"}],"predecessor-version":[{"id":2675,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2196\/revisions\/2675"}],"wp:attachment":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/media?parent=2196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/categories?post=2196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/tags?post=2196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}