<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-46689162362346050</atom:id><lastBuildDate>Tue, 04 Nov 2025 11:20:40 +0000</lastBuildDate><category>eq2</category><category>game programming</category><category>programming</category><category>personal</category><category>games</category><category>hacking</category><category>technology</category><category>nvidia</category><category>ipod</category><category>rise of kunark</category><category>soe</category><category>ultima online</category><category>voice chat</category><category>fun stuff</category><category>iphone</category><category>opinions</category><category>repair</category><category>cwa</category><category>daybreak</category><category>family</category><category>fan faire</category><category>fires</category><category>friends</category><category>github</category><category>guitar hero</category><category>holidays</category><category>interviews</category><category>legos</category><category>libraries</category><category>planetside2</category><category>work</category><category>xbox</category><category>zune</category><category>computer problems</category><category>cycling</category><category>kotk</category><category>linux</category><category>rock band</category><category>shadow odyssey</category><category>star trek</category><category>threading</category><category>cell phones</category><category>commentary</category><category>conferences</category><category>etw</category><category>fastjson</category><category>ipad</category><category>irritations</category><category>login2009</category><category>memes</category><category>mobile development</category><category>molten</category><category>movies</category><category>performance</category><category>psp</category><category>quizzes</category><category>ripping</category><category>scams</category><category>solvers</category><category>star wars</category><category>superluminal</category><category>thor</category><category>web</category><category>wii</category><title>The Joshua Tree</title><description></description><link>http://blog.joshuakriegshauser.com/</link><managingEditor>noreply@blogger.com (Joshua Kriegshauser)</managingEditor><generator>Blogger</generator><openSearch:totalResults>122</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-2050468597755658024</guid><pubDate>Fri, 22 Nov 2024 01:35:00 +0000</pubDate><atom:updated>2024-11-21T17:44:21.226-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">games</category><category domain="http://www.blogger.com/atom/ns#">hacking</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Game Hacking: Part Deux</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrCKjX5MzqCFLqh4ZO5b9ktMUG570uKdsZCThsnb7JlcGQ6lopiDBPwawP6gj9MXgmVPfyi-vFESxBxjwgMeFENRHDP27mui9uxUn-HYFjCFO06b9PIPH3sSK-j6iaCPgVcWrHjvfhOJ-IGaovb1OFs31UqQamVp_RXdTshrDGEnlB5evZpAweexDHQA/s5119/Screenshot2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1435&quot; data-original-width=&quot;5119&quot; height=&quot;113&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrCKjX5MzqCFLqh4ZO5b9ktMUG570uKdsZCThsnb7JlcGQ6lopiDBPwawP6gj9MXgmVPfyi-vFESxBxjwgMeFENRHDP27mui9uxUn-HYFjCFO06b9PIPH3sSK-j6iaCPgVcWrHjvfhOJ-IGaovb1OFs31UqQamVp_RXdTshrDGEnlB5evZpAweexDHQA/w400-h113/Screenshot2.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;I&#39;m still playing through the Assassin&#39;s Creed franchise. But now I&#39;ve finished &lt;a href=&quot;https://en.wikipedia.org/wiki/Assassin%27s_Creed_Rogue&quot; target=&quot;_blank&quot;&gt;Rogue&lt;/a&gt;&amp;nbsp;and have moved on to &lt;a href=&quot;https://en.wikipedia.org/wiki/Assassin%27s_Creed_Unity&quot; target=&quot;_blank&quot;&gt;Unity&lt;/a&gt;&amp;nbsp;which--although released on the same day--are (console) generations apart. However, this also means that the games were created with different &lt;a href=&quot;https://assassinscreed.fandom.com/wiki/Anvil_(game_engine)&quot; target=&quot;_blank&quot;&gt;Ubisoft game engines&lt;/a&gt;: &lt;i&gt;AC:Rogue&lt;/i&gt; was the last game on &lt;i&gt;AnvilNext&lt;/i&gt;&amp;nbsp;and&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&amp;nbsp;was the first game on &lt;i&gt;AnvilNext 2.0&lt;/i&gt;.&lt;/p&gt;&lt;p&gt;Different engines ostensibly have different issues, and older engines can have issues on newer hardware. My &lt;a href=&quot;http://blog.joshuakriegshauser.com/2024/10/game-hacking.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt; describes my issues with&amp;nbsp;&lt;i&gt;AC:Rogue&lt;/i&gt;&amp;nbsp;where the game would stall for seconds at a time with too many CPUs. And I run an AMD Threadripper 5975W with 32 cores/64 hyperthreads--difficult hardware to come by 10-12 years ago when these engines were created.&lt;/p&gt;&lt;p&gt;Turns out that&amp;nbsp;&lt;i&gt;AC:Unity&amp;nbsp;&lt;/i&gt;also has problems with too many CPUs--it crashes to desktop immediately after launch!&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Has this already been fixed?&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The first step was to see if anyone else has run into this problem and if they&#39;ve fixed it. Turns out I was in luck. A reddit user running on the same CPU &lt;a href=&quot;https://www.reddit.com/r/ubisoft/comments/16pgvwl/ac_unity_crash_on_startup_on_highend_pc/&quot; target=&quot;_blank&quot;&gt;had the same issue&lt;/a&gt;. Unfortunately, the &lt;a href=&quot;https://youtu.be/5abOt0V59d0?t=425&quot; target=&quot;_blank&quot;&gt;workaround&lt;/a&gt; was pretty sucky--disable CPU cores in the BIOS. I definitely don&#39;t want to have to reboot before and after playing the game.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Fortunately now we know what the problem is--too many CPUs. The fix in the previous blog post was similar, but since this is the second game I&#39;ve had to fix in the same way, let&#39;s see if we can make a fix that is more generic.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Let&#39;s make a generic tool! But how?&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Enter &lt;a href=&quot;https://github.com/microsoft/Detours&quot; target=&quot;_blank&quot;&gt;Microsoft Detours&lt;/a&gt;--an amazing tool for hacking (and even some legitimate uses). Basically the plan is to create a .DLL file that we can inject into the process that will intercept (or hook) some system calls and &lt;strike&gt;lie&lt;/strike&gt;&amp;nbsp;&lt;i&gt;present an alternative set of facts&lt;/i&gt; to the game. This .DLL can be used with nearly any game, so we could go back and use it with &lt;i&gt;Rogue&lt;/i&gt;&amp;nbsp;to fix it without hacking the assembly language.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;In the previous post, we pointed out two system calls (&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo&quot; target=&quot;_blank&quot;&gt;GetSystemInfo&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getnativesysteminfo&quot; target=&quot;_blank&quot;&gt;GetNativeSystemInfo&lt;/a&gt;) that tell us the number of CPUs, but this is just the tip of the iceberg. It turns out that&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&amp;nbsp;uses many more system calls than this. And for our generic tool to support all games, we should at least figure out what&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&amp;nbsp;is using and support those calls.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;But where do we draw the line? In the past 20+ years we have gone from single CPU to multiple SMP CPUs, to &lt;a href=&quot;https://en.wikipedia.org/wiki/Non-uniform_memory_access&quot; target=&quot;_blank&quot;&gt;NUMA&lt;/a&gt;. Machines are available these days that have over 64 CPUs. AMD is preparing to launch servers with 256 cores next year (&lt;a href=&quot;https://www.titancomputers.com/AMD-EPYC-Genoa-9004-Series-up-to-256-Cores-Workstation-PC-s/1270.htm?srsltid=AfmBOooBpQI-CxbgxwvEclk-konkBy48v4SbI91rBFruBeMQyx5Ycuqo&quot; target=&quot;_blank&quot;&gt;order now!&lt;/a&gt;). Now it&#39;s unlikely people will run games on these machines, but that was probably the thinking back when 32+ CPU machines were unheard-of. The point is that modern Operating Systems have evolved and added system calls to support &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support&quot; target=&quot;_blank&quot;&gt;NUMA&lt;/a&gt;, &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups&quot; target=&quot;_blank&quot;&gt;CPU Groups&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/cpu-sets&quot; target=&quot;_blank&quot;&gt;CPU Sets&lt;/a&gt;. While it&#39;s conceivable that games could call some or all of these, let&#39;s assume that they won&#39;t. But maybe we can set up some logging for these functions to see if they&#39;re ever called.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The bigger issue is probably going to be &lt;i&gt;affinity&lt;/i&gt;. Simply put, &lt;i&gt;affinity&lt;/i&gt;&amp;nbsp;is a list of CPUs that a process (or threads) can run on. Typically this list is given as a bit mask since each bit can effectively represent a CPU. Since (historically) the number of CPUs was small, this was fine for a 32-bit value, but now we have over 32 CPUs! No longer can a single 32-bit value represent an affinity mask. Older 32-bit applications typically used a 32-bit value, but newer 64-bit applications generally use 64 bits (but bugs may exist from porting previous code, etc.).&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Intercepting Functions&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The Detours wiki has a pretty simple minimum example for &lt;strike&gt;hooking&lt;/strike&gt;&amp;nbsp;&lt;i&gt;instrumenting &lt;/i&gt;the Sleep function. Let&#39;s alter it for GetSystemInfo, one of the functions that we&#39;ll need to intercept. (&lt;a href=&quot;https://gist.github.com/jkriegshauser/25a18ae1f81d5bcf3324e9ab08780d92&quot; target=&quot;_blank&quot;&gt;Source Listing&lt;/a&gt;)&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This listing will compile into a .DLL that, when loaded by an application, will override calls to GetSystemInfo and allow us to alter the responses.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Once DetourTransactionCommit() is called in our InstallDetours() function our hook is in place:&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj3mxsY0_8vYviGwNqMjWbuqRJKBtx5NYb-fVTtnuf8nPVULXGEWOq5ON4sMWQ8sa3GDA28t0d8ylhDGyEy1JDhXwXWjICrfpiwBM_kJbxCU2gyNjhvBH9tS4fQWWxwcZJQC76svx6oAbKyWWXB8hbMhdJpNK7CkItJY-j_tbcad_UhTrTVu9d-6U4qWA&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;228&quot; data-original-width=&quot;574&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj3mxsY0_8vYviGwNqMjWbuqRJKBtx5NYb-fVTtnuf8nPVULXGEWOq5ON4sMWQ8sa3GDA28t0d8ylhDGyEy1JDhXwXWjICrfpiwBM_kJbxCU2gyNjhvBH9tS4fQWWxwcZJQC76svx6oAbKyWWXB8hbMhdJpNK7CkItJY-j_tbcad_UhTrTVu9d-6U4qWA=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now whenever GetSystemInfo is called, our version is called instead. We just happen to call the original and then modify the number of processors:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhXFnruO41dANFc5rT_BTgsfovViiZd00WekxvISX7OBrIfMIP2RMza9Qwy84F3Oh32OBOAABTs65JnbhhOq3OsROqW9obYYElq3qygUqlyGlBtE6iHr7Um20Owqw73WRUQePyCqXjf8uBdfyq8K8gDot3DwafzZeQDfcpoyizh7pLcpLBzyYvhnCKjHw&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;210&quot; data-original-width=&quot;512&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhXFnruO41dANFc5rT_BTgsfovViiZd00WekxvISX7OBrIfMIP2RMza9Qwy84F3Oh32OBOAABTs65JnbhhOq3OsROqW9obYYElq3qygUqlyGlBtE6iHr7Um20Owqw73WRUQePyCqXjf8uBdfyq8K8gDot3DwafzZeQDfcpoyizh7pLcpLBzyYvhnCKjHw=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The next step is that we need to get the game to load our new DLL. Unfortunately, this is easier said than done. Games these days typically don&#39;t want hackers messing with them and they go to great lengths to try to prevent that (just as I did when working on my games).&amp;nbsp;&lt;i&gt;AC:Rogue&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;didn&#39;t have any protections, but&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;has a multiplayer element to it, and multiplayer games typically make attempts to curb hacking/cheating. Now I&#39;m just trying to get the game to &lt;b style=&quot;font-style: italic;&quot;&gt;run &lt;/b&gt;but it doesn&#39;t know that.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Detours has (at least) two tools that can be used. The &lt;a href=&quot;https://github.com/microsoft/Detours/wiki/SampleWithdll&quot; target=&quot;_blank&quot;&gt;WithDll&lt;/a&gt; tool can be used to launch an application and inject a DLL before it fully loads. Unfortunately this method didn&#39;t work with&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;because of how it&#39;s launched. I purchased the game through Steam, so Steam launches&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;, which sees that it wasn&#39;t launched from Ubisoft Connect, so it launches Ubisoft Connect and exits, which does a few things (cloud sync for instance) and then re-launches&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;. Since I cannot control how the game is launched from Ubisoft Connect, this didn&#39;t pan out. Interestingly though the app &lt;i&gt;did&lt;/i&gt; launch with the injected DLL.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The other tool is &lt;a href=&quot;https://github.com/microsoft/Detours/wiki/SampleSetdll&quot; target=&quot;_blank&quot;&gt;SetDll&lt;/a&gt;. This tool can modify an executable file to &lt;i&gt;always&lt;/i&gt;&amp;nbsp;load a new DLL. This sounds like exactly what we need! Unfortunately, this didn&#39;t work either since&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;has hack-prevention checks that noticed that the executable was modified and refused to run.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Frustrated, I started looking in to other ways to inject the DLL. SetDll works on DLLs too, so if I can catch it early enough in the loading process, I could use a different DLL as an injection vector. I fired up &lt;a href=&quot;https://learn.microsoft.com/en-us/sysinternals/downloads/procmon&quot; target=&quot;_blank&quot;&gt;Process Monitor&lt;/a&gt;&amp;nbsp;and fiddled with the filters to see what the&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;process was doing when it started. Turns out it ships with a DLL that is loaded very early, and that DLL doesn&#39;t complain about modifications. Eureka! I have my injection vector.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;I added some simple logging (using &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-outputdebugstringw&quot; target=&quot;_blank&quot;&gt;OutputDebugString&lt;/a&gt;&amp;nbsp;that I could view with the &lt;a href=&quot;https://learn.microsoft.com/en-us/sysinternals/downloads/debugview&quot; target=&quot;_blank&quot;&gt;DebugView utility&lt;/a&gt;) to my simple example to make sure that my function was being called (since&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&amp;nbsp;also detects debuggers and terminates the process if one is found). Good news--it was being called! But of course, the game was still crashing. So while this was enough to essentially fix&amp;nbsp;&lt;i&gt;AC:Rogue&lt;/i&gt;, the newer engine does different things and will need more work.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Affinity Functions&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;As I mentioned above, the functions that control affinity are probably in use. The Microsoft article about &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups&quot; target=&quot;_blank&quot;&gt;Processor Groups&lt;/a&gt;&amp;nbsp;has a good list of functions that control affinity and scheduling. My process was to first add logging for these functions to see if they were being called. The logging would include what arguments that&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;was passing to the functions as well as what the functions returned. Turns out that several of them were being called. Even though GetSystemInfo (and GetNativeSystemInfo) were intercepted and showing a lower number of CPUs, the affinity functions were happily reporting affinity across a larger number of CPUs.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The next process was to change the behavior of these affinity functions to only return bits set for the number of processors that we were emulating.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;An interesting function that it turns out&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;was also calling is &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadidealprocessor&quot; target=&quot;_blank&quot;&gt;SetThreadIdealProcessor&lt;/a&gt;&amp;nbsp;(and the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadidealprocessorex&quot; target=&quot;_blank&quot;&gt;extended version&lt;/a&gt;). This function gives the Windows scheduler a hint about which CPU to schedule the thread on, but it&#39;s not as strong as affinity. I also intercepted this function to only allow the processors in our emulated set.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Logical Processor Info&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;In my work hacking&amp;nbsp;&lt;i&gt;AC:Rogue&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;I discovered that it was optionally looking for and calling a function called &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation&quot; target=&quot;_blank&quot;&gt;GetLogicalProcessorInformation&lt;/a&gt;&amp;nbsp;(which also has an &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex&quot; target=&quot;_blank&quot;&gt;extended version&lt;/a&gt;). CPUs these days are very complicated: they have multiple layers of caching (some of which are shared with other cores), can share hyperthreads with other cores, not to mention the NUMA relationships. These functions are the means to access this data on Windows, and as you might expect, are &lt;i&gt;quite complicated&lt;/i&gt;. The extended version is even &lt;i&gt;more &lt;/i&gt;complicated allowing different means of querying data and returning variable-sized information.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;It turns out that&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;was also calling these functions, so they are another one that I had to wrangle.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;For the original function, all of the data is presented at the same time, so the function returns the same data every time it is called (at least, it is highly unlikely that it would change while the game is running). Therefore, it is easily cached. The first time my intercepted function is called, it queries all of the host data, chops it down to only include our emulated processors, and saves it off so that every subsequent time that we&#39;re called we just return the same data.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Unfortunately, the extended function allows specifying a Relationship Type to query, and the data is drastically different based on that query type. My solution here was to cache the chopped-down host data and remember the Relationship Type along with it. So if a different Relationship Type was queried I would throw away the cache and rebuild it. This however was much more complicated because of the variable-sized data. I essentially had to filter the data in place and then copy the filtered data into the cache. For this it&#39;s probably better to look at the code directly: (&lt;a href=&quot;https://github.com/jkriegshauser/CpuLimiter/blob/4d35219b0d4b11cc4a0a6c0ecdd62e4414c4eb1a/CpuLimiter.c#L592&quot; target=&quot;_blank&quot;&gt;intercept function&lt;/a&gt;, &lt;a href=&quot;https://github.com/jkriegshauser/CpuLimiter/blob/4d35219b0d4b11cc4a0a6c0ecdd62e4414c4eb1a/CpuLimiter.c#L451&quot; target=&quot;_blank&quot;&gt;cache builder&lt;/a&gt;)&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Does it run now?&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;YES! 🎉&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;In the end, I ended up hooking 12 functions (but three--the Process Group functions--weren&#39;t called by&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&amp;nbsp;and therefore only log).&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgzb5C2P9KTEw5hiaw2soGQTcEcyZDG17XCpxc-4EJipglwTfPX3yl0-50A9zxAyEvzIuFazm4z0B8GKia6pr9uoqpa_eSQrfGzCTzTUWidQrvJB8GejbzoO1gBLq-NvjMOIwA1ZhMFrz2T0EKBdcEvSbJH4pb75qoMNDKttttNltxnxso4EmGhrt4XgA&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;251&quot; data-original-width=&quot;361&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgzb5C2P9KTEw5hiaw2soGQTcEcyZDG17XCpxc-4EJipglwTfPX3yl0-50A9zxAyEvzIuFazm4z0B8GKia6pr9uoqpa_eSQrfGzCTzTUWidQrvJB8GejbzoO1gBLq-NvjMOIwA1ZhMFrz2T0EKBdcEvSbJH4pb75qoMNDKttttNltxnxso4EmGhrt4XgA=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Finally, I can play the game for a bit.&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;But... oh no. There&#39;s another problem.&lt;/p&gt;&lt;h1 style=&quot;text-align: left;&quot;&gt;The Video Problem&lt;/h1&gt;&lt;p style=&quot;text-align: left;&quot;&gt;I have a Samsung Odyssey G9 49&quot; monitor. It&#39;s aspect ratio is 32:9 and I&#39;m playing a game originally designed to run on consoles that output 16:9. However, the PC port of the game itself beautifully figures out the correct aspect and does pretty well with the super ultrawide display, as evidenced in the screenshot at the top of this post.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The problem is the full-motion videos that it uses for cutscenes. They are originally designed for 16:9 and are stretched out to 32:9--double the width they should be. It&#39;s almost breaking my brain trying to watch them and figure out what&#39;s going on.&lt;/p&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0jzMXutfXU2syRZIwqktWvZoeLlgOpwUEDSgA5t4fKNtYfMWAY1MwZPDI-yiWb-YwkFZvORuWH82Z8ZJIOf-Xq-jqhupjFWhxBC4CznXLv33Qz9zUK6q6FWih_eJfHS3RvWdpnnCYDPM20mqJCtjky_LgKWo2BJZP9v-FhJokuL4_lgPT94PBH7_C0g/s5119/Wat.jpg&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1434&quot; data-original-width=&quot;5119&quot; height=&quot;113&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0jzMXutfXU2syRZIwqktWvZoeLlgOpwUEDSgA5t4fKNtYfMWAY1MwZPDI-yiWb-YwkFZvORuWH82Z8ZJIOf-Xq-jqhupjFWhxBC4CznXLv33Qz9zUK6q6FWih_eJfHS3RvWdpnnCYDPM20mqJCtjky_LgKWo2BJZP9v-FhJokuL4_lgPT94PBH7_C0g/w400-h113/Wat.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;A very wide Mr. Templar tied to a tree&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Can I fix this too?&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Bink Video&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.radgametools.com/bnkmain.htm&quot; target=&quot;_blank&quot;&gt;Bink&lt;/a&gt; is a popular video encoder and player software package for games. Developed by &lt;a href=&quot;https://www.radgametools.com/&quot; target=&quot;_blank&quot;&gt;RAD Game Tools&lt;/a&gt; (now owned by Epic and rechristened as Epic Game Tools), Bink was originally released way back in 1999.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Bink is pretty easy to integrate and very fast. I integrated it in to EverQuest 2 back in 2006, so I&#39;m somewhat familiar with the API.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Turns out that Ubisoft uses it in &lt;i&gt;AC:Unity&lt;/i&gt;, though a much newer version than the one I used. Fortunately a lot of the API seems to have remained consistent. And all I want to do is prevent the videos from being stretched.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Research&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The first step is to figure out how&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;is using Bink. Fortunately I already have the means to do this, just like I did with the CPU Windows system functions--hook the API calls and see how they&#39;re being called. Bink is loaded as a DLL, so theoretically I can hook it too.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;And the initial tests are promising. I used Microsoft&#39;s dumpbin tool to list the exports from the Bink DLL. Pretty much any application is guaranteed to call BinkOpen, so that&#39;s the first function that I hook. And testing is quite quick since the first thing&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;shows is the Ubisoft logo video.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Unfortunately, this is where things hit a snag. I&#39;ve managed to hook a few functions that give me useful information (I can see BinkRegisterFrameBuffers called for instance), but none of the functions with names that sound promising are ever called (BinkCopyToBuffer, BinkCopyToBufferRect, BinkBufferOpen, BinkBufferSetResolution, BinkBufferSetScale). I ended up hooking 20+ of the most interesting sounding functions only to see a tiny few of them being called. How is it accessing the data without calling BinkCopyToBuffer?!?&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;It&#39;s time to get dirty. I can&#39;t attach a debugger because of the anti-hack measures (and it will take way too long to reverse engineer those), but I need to see it in a debugger when it&#39;s trying to access the data. So I&#39;ll make the game hang when it tries to access the framebuffer!&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;It turns out this isn&#39;t too complicated: I register an unhandled exception filter that just sleeps infinitely when a crash happens, and then I call &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect&quot; target=&quot;_blank&quot;&gt;VirtualProtect&lt;/a&gt;&amp;nbsp;to tell Windows to make the framebuffer data no-access--any attempt to read or write to it will cause a crash. However, I have to allow Bink to write the data in to the framebuffer, I just want to see where it&#39;s read back out, so I have to allow access to the BinkDoFrame function.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;As soon as the game hangs (without displaying the first video frame) I use Task Manager to write a full dump. But the dump is confusing. I have no symbols, no function names, nothing to really give me any clues as to what&#39;s happening except the assembly code. And it looks like&amp;nbsp;&lt;i&gt;AC:Unity&amp;nbsp;&lt;/i&gt;is reading the data directly out of the framebuffers.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;That&#39;s exactly what it&#39;s doing.&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;is calling BinkRegisterFrameBuffers which means it is allocating its own framebuffers and undoubtedly retaining the pointers. BinkDoFrame will decompress the video stream into the framebuffer, and then&amp;nbsp;&lt;i&gt;AC:Unity&amp;nbsp;&lt;/i&gt;is reading the data (probably with a shader) to display on the screen.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;The video fix&lt;/h2&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Looking at the allocated framebuffers, they&#39;re 1920 x 1080 (16:9 ratio) and their shaders are probably the stretching culprit. I&#39;m not a GPU/graphics guy (ironic given where I &lt;a href=&quot;https://nvidia.com/&quot; target=&quot;_blank&quot;&gt;work&lt;/a&gt;) and I have no idea how to break into the game&#39;s shader pipeline. It can be done because there are other mods out there that do, but it will take a long time to learn how to do this. And I want to get back to playing the game, so how do I do it quickly? Is there something I can do to the framebuffers?&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;It turns out I can! I need&amp;nbsp;&lt;i&gt;AC:Unity&amp;nbsp;&lt;/i&gt;to allocate 32:9 framebuffers instead of 16:9, and the first two values in the structure returned from BinkOpen looks like width and height. So the first thing I try is just scaling that width to 32:9. Since buffers historically were powers of two, they typically have a &lt;i&gt;pitch&lt;/i&gt;&amp;nbsp;passed to the decoder that says how many bytes represent a line so that it can skip to the start of the next line (extra bytes would just be off the screen). This helps us out here since the pitch tells Bink to skip about half of the line and just start decoding on the next line. It works!&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEijM_pGwJMpQUdZHqbbdSbevb5FM19cIHdGne8loNH-vBrTR57xM1rhNUMC4aL-YUQ4GbQ7-ApHL0iRqYMzmGONAFQx18CFKpi6QBgJj2J3bnGyq51joz91dHJ1MBWhYyGH7ZWERfMOd29W-bawEmXakKKDToLcf4vfE3S9RjFWVWImDwyT0RzGD2U7Pg&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img data-original-height=&quot;1438&quot; data-original-width=&quot;5119&quot; height=&quot;113&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEijM_pGwJMpQUdZHqbbdSbevb5FM19cIHdGne8loNH-vBrTR57xM1rhNUMC4aL-YUQ4GbQ7-ApHL0iRqYMzmGONAFQx18CFKpi6QBgJj2J3bnGyq51joz91dHJ1MBWhYyGH7ZWERfMOd29W-bawEmXakKKDToLcf4vfE3S9RjFWVWImDwyT0RzGD2U7Pg=w400-h113&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Better?&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Well, kinda. Why is half of the screen green? Hey, but at least the aspect ratio is correct. And it&#39;s not centered, but we can fix that too (probably).&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Alright, so the green screen. Encoding video data as RGB is very inefficient. Remember, Bink is a library developed in 1999 and first used on the PlayStation. It uses the &lt;a href=&quot;https://en.wikipedia.org/wiki/YCbCr&quot; target=&quot;_blank&quot;&gt;YCrCb&lt;/a&gt; color space for its video information to use less space.&amp;nbsp;&lt;i&gt;AC:Unity&amp;nbsp;&lt;/i&gt;likely has a pixel shader that computes the RGB values from the YCrCb planes that the framebuffers are allocated from. The buffers started with zeroed bytes and since the right half of the screen is never written to by the decoder, they remain as zeroed bytes. Well converting the zero values for YCrCb ends up with RGB color #008700, a quite greenish hue.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Looking up the value for &lt;a href=&quot;https://tvone.com/tech-support/faqs/120-ycrcb-values-for-various-colors&quot; target=&quot;_blank&quot;&gt;black in YCrCb&lt;/a&gt;&amp;nbsp;and setting the initial state of the buffers to those values gives us a dark gray. Not quite black. But that link has the minimum Y value as 16, which gives us a dark gray, but trying a Y value of 0 gives us black.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;So the last issue to tackle is the centering.&amp;nbsp;&lt;i&gt;AC:Unity&lt;/i&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;has given Bink the addresses for the framebuffers by calling BinkRegisterFrameBuffers. Bink decodes data into the first line at the start of the buffer, advances the line pointer by &lt;i&gt;pitch &lt;/i&gt;bytes and decodes the next line, so on and so forth. If we can tell it to use a different starting address for the buffer that represents a centered frame, it will move the whole image.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;This is pretty easily done, we can just &lt;a href=&quot;https://github.com/jkriegshauser/CpuLimiter/blob/5470b275320c7dc82bb6d1fd4805efb833977551/CpuLimiter.c#L839&quot; target=&quot;_blank&quot;&gt;calculate&lt;/a&gt; the scaled width of our screen, subtract the width of the video and divide by two to get the framebuffer line offset. We have to do this for each plane separately because the Cr and Cb planes are smaller than the Y/A planes. The tricky thing here though is that Bink requires the framebuffer address to be 16-byte aligned, otherwise a hang occurs in the decoder (speaking from experience).&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Voila!&lt;/p&gt;&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEifcLyPvJi2rvrgkHs76rivWZroCWG9oHKns1sSGHFD7j80ukbPUL0HZAqz30mDTYiyUu4suE5L4riRWXPXZO4-iIHwmTriLXhj4-Q1BvmhsHpKeACxtb1dC_R9u_6HngG6ZZB1tbFOE7e2Yh_8vZl7QgoC00C1MDhOy5zWR-t2L8SsijDosmIpsXViOA&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1433&quot; data-original-width=&quot;5119&quot; height=&quot;113&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEifcLyPvJi2rvrgkHs76rivWZroCWG9oHKns1sSGHFD7j80ukbPUL0HZAqz30mDTYiyUu4suE5L4riRWXPXZO4-iIHwmTriLXhj4-Q1BvmhsHpKeACxtb1dC_R9u_6HngG6ZZB1tbFOE7e2Yh_8vZl7QgoC00C1MDhOy5zWR-t2L8SsijDosmIpsXViOA=w400-h113&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Not so wide now, Mr. Templar!&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Source is &lt;a href=&quot;https://github.com/jkriegshauser/CpuLimiter/tree/ac_unity&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. Uploaded to &lt;a href=&quot;https://www.nexusmods.com/assassinscreedunity/mods/303&quot; target=&quot;_blank&quot;&gt;NexusMods&lt;/a&gt; and now back to playing the game!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;</description><link>http://blog.joshuakriegshauser.com/2024/11/game-hacking-part-deux.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrCKjX5MzqCFLqh4ZO5b9ktMUG570uKdsZCThsnb7JlcGQ6lopiDBPwawP6gj9MXgmVPfyi-vFESxBxjwgMeFENRHDP27mui9uxUn-HYFjCFO06b9PIPH3sSK-j6iaCPgVcWrHjvfhOJ-IGaovb1OFs31UqQamVp_RXdTshrDGEnlB5evZpAweexDHQA/s72-w400-h113-c/Screenshot2.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4759263579348178363</guid><pubDate>Thu, 10 Oct 2024 02:07:00 +0000</pubDate><atom:updated>2024-10-10T09:41:15.356-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">computer problems</category><category domain="http://www.blogger.com/atom/ns#">etw</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">games</category><category domain="http://www.blogger.com/atom/ns#">hacking</category><category domain="http://www.blogger.com/atom/ns#">performance</category><category domain="http://www.blogger.com/atom/ns#">superluminal</category><title>Game Hacking</title><description>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRhoT3Xfiz1f7xlT0hEwJ5uJjIErgnzxvepjsRxzjuzMNhNSZAIMkpg97Sg7r3sLT867X0XIzp2NHU0F9-qTUGkqmD5iAOP_C0DJP742iI1fcm1jSnPlvY4bNaQwJ19tWbB2-Jr5PBjjnWKBpN0lDxlTAOxBjLyyHkEPYvUsVufU-S31ZwptJSlrktPQ/s3207/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;916&quot; data-original-width=&quot;3207&quot; height=&quot;91&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRhoT3Xfiz1f7xlT0hEwJ5uJjIErgnzxvepjsRxzjuzMNhNSZAIMkpg97Sg7r3sLT867X0XIzp2NHU0F9-qTUGkqmD5iAOP_C0DJP742iI1fcm1jSnPlvY4bNaQwJ19tWbB2-Jr5PBjjnWKBpN0lDxlTAOxBjLyyHkEPYvUsVufU-S31ZwptJSlrktPQ/s320/Capture.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I&#39;ve been playing through the Assassin&#39;s Creed franchise in anticipation of &lt;i&gt;Shadows&lt;/i&gt; due out later this year, or well, now pushed to early next year. I really enjoyed &lt;i&gt;Assassin&#39;s Creed IV: Black Flag&lt;/i&gt;, but ran into an issue that some other people had too: &lt;a href=&quot;https://www.reddit.com/r/assassinscreed/comments/14ufz66/trying_to_access_kenways_fleet_and_im_stuck_on/&quot; target=&quot;_blank&quot;&gt;Kenway&#39;s Fleet wouldn&#39;t load&lt;/a&gt;. Fortunately, someone &lt;a href=&quot;https://www.nexusmods.com/assassinscreedivblackflag/mods/426&quot; target=&quot;_blank&quot;&gt;created a patch&lt;/a&gt;&amp;nbsp;that fixed it, and additionally made initial connection to Ubisoft much faster.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Fast forward a bit and I&#39;m playing &lt;a href=&quot;https://en.wikipedia.org/wiki/Assassin%27s_Creed_Rogue&quot; target=&quot;_blank&quot;&gt;&lt;i&gt;Assassin&#39;s Creed: Rogue&lt;/i&gt;&lt;/a&gt;, which is a great game in its own right, but borrows a lot from &lt;i&gt;Black Flag&lt;/i&gt;. However, I started running into an issue: five (or more) second stalls where the game would just freeze (audio would keep playing) and then resume as if nothing happened. I could go a minute or so without these freezes, but sometimes it would be only a second or two after the previous freeze. It was nigh unplayable.&lt;/p&gt;&lt;p&gt;Searching around, other people didn&#39;t seem to report the same problems that I did. But my PC is pretty unique: it&#39;s an AMD Threadripper with 64 logical cores and 128 GB of RAM. That&#39;s a lot of hardware for a game released 10 years ago. Looking in Task Manager, I see that the game has about 280 threads. That&#39;s a lot! I wondered if the game is creating a number of worker threads per logical CPU, but perhaps isn&#39;t optimized for such a high number of CPUs. Designing software that runs well fully using 4 CPUs is a lot different than software that fully uses 64 CPUs.&lt;/p&gt;&lt;p&gt;Alright, so my working theory: the game has a contention problem that shows up with tons of threads.&lt;/p&gt;&lt;p&gt;How to test that theory? I fire up one of my favorite profilers: &lt;a href=&quot;https://superluminal.eu/&quot; target=&quot;_blank&quot;&gt;Superluminal&lt;/a&gt;. Unfortunately, my ability to seriously debug performance issues is going to be hampered by the fact that I don&#39;t have any debugging information (or source code) for the game in question. But Superluminal can tell me what the OS is doing, and what requests the game is making of the OS.&lt;/p&gt;&lt;p&gt;So I fire up Superluminal and tell it to start watching &quot;ACC.exe&quot;, the &lt;i&gt;AC:Rogue&lt;/i&gt; executable. Sure enough, within a few seconds I get a 5 second stall. After it recovers, I head back to superluminal to stop the tracing and start looking at it. The stall is clear as day and I have selected it here:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEirlA8pTqXH-rY0Eawq3uEqCUdHRlGVUfgVJr36Z-qKKFsiT9_8soZXg77WpTDTm_wF678aNd3Ma9DSNpFyTeSsVlHAb77M0oJvj0Kl3HWb97Qs26CKbx4CYLVP1ZWAWm6URWajWIKpPLS41qNZXSJAN4ail7i7R8Cq-ZiSIONCAitrxqNvvun1XfUwvg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1060&quot; data-original-width=&quot;1593&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEirlA8pTqXH-rY0Eawq3uEqCUdHRlGVUfgVJr36Z-qKKFsiT9_8soZXg77WpTDTm_wF678aNd3Ma9DSNpFyTeSsVlHAb77M0oJvj0Kl3HWb97Qs26CKbx4CYLVP1ZWAWm6URWajWIKpPLS41qNZXSJAN4ail7i7R8Cq-ZiSIONCAitrxqNvvun1XfUwvg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;From this, it&#39;s pretty apparent that the main thread is running hard during the stall, and from the function list in the bottom-left corner, I see it&#39;s trying to lock and unlock &quot;critical sections&quot; which are Windows mutex-like objects. Note that before and after the stall the main thread spends a decent amount of time synchronizing (the red part of the red/green CPU activity graph). Let&#39;s scroll through the other threads and see if we see anything else interesting.&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjzn4PhseiRaD3LKL51YO9zX7AQr2ucDykcERWTboJ58zQuTOdmK7yg0OSLYIJie8V74EPRkuAKHXA0q8RJnpS_WE8krJBCK0HP6N81J4fkkt3T37aoTBH1PiDWQhZk1bTl6gCzDcggHhX2qbvf5q31m9Ap_DMS2d30Gdim06ASeq6RxR_KJOAoTmwdSw&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1060&quot; data-original-width=&quot;1593&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjzn4PhseiRaD3LKL51YO9zX7AQr2ucDykcERWTboJ58zQuTOdmK7yg0OSLYIJie8V74EPRkuAKHXA0q8RJnpS_WE8krJBCK0HP6N81J4fkkt3T37aoTBH1PiDWQhZk1bTl6gCzDcggHhX2qbvf5q31m9Ap_DMS2d30Gdim06ASeq6RxR_KJOAoTmwdSw&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Aha! Thread 86036 has a CPU chart that is all blue. This is Superluminal&#39;s visual language for &quot;preemption&quot; that is, the thread was running but the Windows scheduler decided that running something else was more important. Also interesting to note that the other worker threads above are completely blocked on synchronization. They&#39;re just waiting during this stall.&lt;p&gt;&lt;/p&gt;&lt;p&gt;Looking through some of the other threads there&#39;s not really much that is interesting. A few threads are sleeping and waking up periodically. But the main thread is running hard, this thread 86036 is preempted, and pretty much everything else stops while this is happening. It&#39;s possible that Windows&amp;nbsp; (for some unknown reason) chooses to stop running this thread 86036 and instead runs the main thread.&lt;/p&gt;&lt;p&gt;Superluminal gets information about every process running on the system at that time, and the only thing using any CPU is ACC.exe, the game we&#39;re profiling.&lt;/p&gt;&lt;p&gt;For additional information, I also captured system state using Event Tracing for Windows by utilizing the &lt;a href=&quot;https://github.com/google/UIforETW&quot; target=&quot;_blank&quot;&gt;UIforETW&lt;/a&gt;&amp;nbsp;tool by &lt;a href=&quot;https://randomascii.wordpress.com/&quot; target=&quot;_blank&quot;&gt;Bruce Dawson&lt;/a&gt;. Looking at the capture I was able to find a different stall in the same thread:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjWQNnVZfrI6LR0kNWYzVt-0YbYAPR_I4fTfEw2_1ty-DIeZszWFXNCnj0BPFEGPWnRQ2nwlam0F0zVSepLOgLfe8YPlv-2McNsEmeVtATXSIlZWTieiYpWaTOAISSkdLW-NJbCXAEQ7oyVw7MHYmiKOjvhKXnkJ1GvcpzVIaqzPE2HI58ID6REpvIZGw&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1087&quot; data-original-width=&quot;1887&quot; height=&quot;184&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjWQNnVZfrI6LR0kNWYzVt-0YbYAPR_I4fTfEw2_1ty-DIeZszWFXNCnj0BPFEGPWnRQ2nwlam0F0zVSepLOgLfe8YPlv-2McNsEmeVtATXSIlZWTieiYpWaTOAISSkdLW-NJbCXAEQ7oyVw7MHYmiKOjvhKXnkJ1GvcpzVIaqzPE2HI58ID6REpvIZGw&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Check out the highlighted line: the thread was ready to run for 4,747,081.6 microseconds (4.7 seconds!) before it ran, and the Readying Thread of -1 indicates that it was in fact preemption. Not immediately apparent from the screenshot (but from further digging through the UI) is that both the main thread and thread 86036 have the same Ideal CPU (0) and Priority (15)&lt;p&gt;&lt;/p&gt;&lt;p&gt;Unfortunately it&#39;s still not super apparent exactly what is causing this. A possibility is affinity--which CPUs a thread is allowed to run on--but it&#39;s not clear how to check this. Most threads have 0 as their Ideal CPU, so I&#39;m not sure that this value is actually used by Windows 11. But the Priority values are interesting. Most of the threads in the process have Priority values of 13 and 15, which would correspond to a &quot;High&quot; priority class and &quot;normal&quot; and &quot;time critical&quot; priorities respectively. If both the main thread and thread 86036 share a &quot;time critical&quot; priority, they could affect each other running.&lt;/p&gt;&lt;p&gt;But my initial thought was that 280+ threads is a lot, especially if it&#39;s running some or all of them as time-critical priority. Let&#39;s see if we can get the game to create fewer threads. Unfortunately, this is easier said than done.&lt;/p&gt;&lt;p&gt;Most compute-intensive programs trying to max-out a machine (like games) will typically create at least one thread per CPU, so maybe we can lie to the game about how many CPUs there are. There are actually several API calls that it could be making to determine this, but the most common one would be &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;GetSystemInfo&lt;/span&gt;&lt;/a&gt;&amp;nbsp;(side note: modern C++ Runtime implementations use &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getnativesysteminfo&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;GetNativeSystemInfo&lt;/span&gt;&lt;/a&gt;, but this function does not appear to be used by ACC.exe). Fortunately the game executable doesn&#39;t appear to be cryptographically signed, so we should be able to patch it.&lt;/p&gt;&lt;p&gt;The next step is to disassemble the game so that we can find the places where it might be trying to find out the number of CPUs. For this I used &lt;a href=&quot;https://hex-rays.com/ida-free&quot; target=&quot;_blank&quot;&gt;IDA Freeware&lt;/a&gt;. While the disassembly is quick, the analysis happens in the background and takes a little while. Once analysis was done, I looked at the Imports and found the &lt;span style=&quot;font-family: courier;&quot;&gt;GetSystemInfo&lt;/span&gt;&amp;nbsp;function. There were several cross-references to places that were calling it (I took the liberty of renaming the functions based on a cursory glance of what they were doing):&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjwn7q_vpODG6hvV7Uj_vM_mZum5HiwECxnSAlKsS7rwoNFEb2iAHHHOk_GuvmbI0dIR5f2nAEhcnPJCaTvI3yxoueoKr2n-T7gh8umWfYkgcHvWYanuXD4Uct5ElKzNfPG1lDltx3KK4-4ZCIWLG2wQdPAQtZLo1512ciaOm1cSKmhjIiR2WVO6iFxqA&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;824&quot; data-original-width=&quot;1259&quot; height=&quot;209&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjwn7q_vpODG6hvV7Uj_vM_mZum5HiwECxnSAlKsS7rwoNFEb2iAHHHOk_GuvmbI0dIR5f2nAEhcnPJCaTvI3yxoueoKr2n-T7gh8umWfYkgcHvWYanuXD4Uct5ElKzNfPG1lDltx3KK4-4ZCIWLG2wQdPAQtZLo1512ciaOm1cSKmhjIiR2WVO6iFxqA&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;Let&#39;s take a look at the second entry, the one that I renamed to &lt;span style=&quot;font-family: courier;&quot;&gt;getSystemInfo2&lt;/span&gt;. I was really impressed with IDA here: it understands Windows API structures and was able to figure out how members of the structures were being used:&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhFBBIcDP8Wf2GGxgoSLZRBaQpRqufjReETWLXIXHMkoMsFYhCqkY-sYeSR3SqK4rIL3QEDt7OmTXk17rcmCHVAruCOjs8_-rhPZxxYZLV-qvGtVrMOu8yw-1qa4cpfk88Vgv-jAUEEobYVUXK5oUnKB962xbJ_eQOyZTAA-4EAr-nIXyj-GDlAaFZL-g&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;435&quot; data-original-width=&quot;916&quot; height=&quot;152&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhFBBIcDP8Wf2GGxgoSLZRBaQpRqufjReETWLXIXHMkoMsFYhCqkY-sYeSR3SqK4rIL3QEDt7OmTXk17rcmCHVAruCOjs8_-rhPZxxYZLV-qvGtVrMOu8yw-1qa4cpfk88Vgv-jAUEEobYVUXK5oUnKB962xbJ_eQOyZTAA-4EAr-nIXyj-GDlAaFZL-g&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Note that &lt;span style=&quot;font-family: courier;&quot;&gt;dwNumberOfProcessors&lt;/span&gt; is read into the edx register here and stored in various locations. Instead of reading the value from the structure, I want to lie to the game: I want it to see a hard-coded value of 8 CPUs. So now I need to change the bytes (8B 54 24 50) which is the machine code for &quot;&lt;span style=&quot;font-family: courier;&quot;&gt;mov edx, [rsp+68h+SystemInfo.dwNumberOfProcessors]&lt;/span&gt;&quot; to... something else. Something that just gives me the value of 8 in &lt;i&gt;edx&lt;/i&gt;. Unfortunately the free version of IDA doesn&#39;t do assembling, so I can&#39;t write new code here and expect IDA to build me a new executable.&lt;/p&gt;&lt;p&gt;Instead, we&#39;re going to do it the hard way: I need to figure out how to get the value of 8 into &lt;i&gt;edx&lt;/i&gt; but in 4 bytes or less. Since IDA won&#39;t help me out here, I&#39;ll use an &lt;a href=&quot;https://shell-storm.org/online/Online-Assembler-and-Disassembler/&quot; target=&quot;_blank&quot;&gt;online assembler&lt;/a&gt;&amp;nbsp;to try some things, and then a hex editor to change the actual bytes.&lt;/p&gt;&lt;p&gt;Well, let&#39;s try the obvious instruction: &quot;&lt;span style=&quot;font-family: courier;&quot;&gt;mov edx, 8&lt;/span&gt;&quot;&lt;/p&gt;&lt;p&gt;Unfortunately this results in 5 bytes (ba 08 00 00 00), which is difficult to stuff into 4 bytes. What&#39;s interesting is that the full immediate 32-bit value representing &quot;8&quot; is present in the machine code, which makes some sense since we&#39;re writing to a 32-bit register.&lt;/p&gt;&lt;p&gt;This gave me an idea. What is the machine code to move 8 into the lowest byte of &lt;i&gt;edx&lt;/i&gt; (the &lt;i&gt;dl&lt;/i&gt; register)? Turns out this is only 2 bytes (b2 08)! The problem is that the other 24 bits of our &lt;i&gt;edx&lt;/i&gt; register are garbage. Maybe we can clear the register with an &lt;i&gt;xor&lt;/i&gt; (exclusive-or) instruction? If you &lt;i&gt;xor&lt;/i&gt; a register against itself the value is always 0. Let&#39;s try that: &lt;span style=&quot;font-family: courier;&quot;&gt;xor edx, edx&lt;/span&gt;--this produced machine code (31 d2).&lt;/p&gt;&lt;p&gt;Perfect, so we have our solution:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj1ftIOg_PrFDYoIFXiCOocZTQMqDK4papGOSg5Lj7abnVcLi6NVMd3jX2Ft1Z2rv0kokhqVEwW_YwPzmj-ajIqG39BPIY-_h8uden1KNp_yzBAMnX-QEQo7Q-QjIcQ1h8329BvF76NB606ikJLiUGwwObl7H5tkTrYXGWF6aRN9vz8gDSBFTHTtb5V5g&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;502&quot; data-original-width=&quot;884&quot; height=&quot;182&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj1ftIOg_PrFDYoIFXiCOocZTQMqDK4papGOSg5Lj7abnVcLi6NVMd3jX2Ft1Z2rv0kokhqVEwW_YwPzmj-ajIqG39BPIY-_h8uden1KNp_yzBAMnX-QEQo7Q-QjIcQ1h8329BvF76NB606ikJLiUGwwObl7H5tkTrYXGWF6aRN9vz8gDSBFTHTtb5V5g&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Then I&#39;ll just use my trusty hex editor plugin for VSCode to search for the preceding bytes and write over the old bytes:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjxhwtEla_tSDexGzFARk2FKlwekr5LDfm4hs5pQpzoQeQ3vP0bTd1T0FUS5Hyyxxhhzlf4gAMGAl12ucDDURPSMk5IKKTgLhCW2WNBOaHV6FB-x7i3M0UgmRLdY76ff8JAG8Vsq6ys6IApyOeBBvh3PWIO9lIrpwzbaoN9zgdtCbpFyd8f8Tx6hmXhyQ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;349&quot; data-original-width=&quot;691&quot; height=&quot;162&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjxhwtEla_tSDexGzFARk2FKlwekr5LDfm4hs5pQpzoQeQ3vP0bTd1T0FUS5Hyyxxhhzlf4gAMGAl12ucDDURPSMk5IKKTgLhCW2WNBOaHV6FB-x7i3M0UgmRLdY76ff8JAG8Vsq6ys6IApyOeBBvh3PWIO9lIrpwzbaoN9zgdtCbpFyd8f8Tx6hmXhyQ&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;We just need to apply this to each location in our cross-reference list above.&lt;/p&gt;&lt;p&gt;Working through the list, we run into a problem with the function that I&#39;ve called &quot;&lt;span style=&quot;font-family: courier;&quot;&gt;getMemoryAndCpuInfo&lt;/span&gt;&quot;. The &lt;span style=&quot;font-family: courier;&quot;&gt;dwNumberOfProcessors&lt;/span&gt; value is written to a different register: &lt;i&gt;r11d&lt;/i&gt;. This is an extended register for 64-bit processors and needs 5 machine code bits to read the value into it.&lt;/p&gt;&lt;p&gt;Unfortunately, our trick isn&#39;t working here. Assembling &quot;&lt;span style=&quot;font-family: courier;&quot;&gt;xor r11d,r11d; mov r11b, 8&lt;/span&gt;&quot; takes 6 bytes.&lt;/p&gt;&lt;p&gt;Let&#39;s try a different approach: &lt;i&gt;push&lt;/i&gt; and &lt;i&gt;pop&lt;/i&gt; the value, which uses the stack. We &lt;i&gt;really &lt;/i&gt;don&#39;t want to make any stack changes as moving the stack pointer will cause issues with all of the following instructions that read/write from the stack. But if we immediately reset the stack pointer by popping the value, it should be fine. So let&#39;s try that:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgvrtunFCY1_DxvVu3UJP0z2mTmNA7v_PGgDqCZ47TmMPbkJ67EcpkFlVesFDKWhfQazjryzAI1aOehNr0B5GIkSQK7IaFACCPpcpFh9YzSXLXC0e6p1YA8Am65qP56F0i_l9sVaGfcz0oRwIjt3Kjl34BoamyaOj2kVsLthahRU0AT7seZI4u3_3ml-Q&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;510&quot; data-original-width=&quot;897&quot; height=&quot;182&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgvrtunFCY1_DxvVu3UJP0z2mTmNA7v_PGgDqCZ47TmMPbkJ67EcpkFlVesFDKWhfQazjryzAI1aOehNr0B5GIkSQK7IaFACCPpcpFh9YzSXLXC0e6p1YA8Am65qP56F0i_l9sVaGfcz0oRwIjt3Kjl34BoamyaOj2kVsLthahRU0AT7seZI4u3_3ml-Q&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Perfect! The push/pop is only 4 instruction bytes, so we had to add a &quot;&lt;i&gt;nop&lt;/i&gt;&quot; or no-operation instruction (only one byte) to bring us up to our total of 5.&lt;/p&gt;&lt;p&gt;Our next wrinkle happens in the function that I&#39;ve dubbed &lt;span style=&quot;font-family: courier;&quot;&gt;getProcessorTopology&lt;/span&gt;. While this function is also calling &lt;span style=&quot;font-family: courier;&quot;&gt;GetSystemInfo&lt;/span&gt; and using &lt;span style=&quot;font-family: courier;&quot;&gt;dwNumberOfProcessors&lt;/span&gt; (easily patched by our previous methods), it&#39;s also doing something further:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjUql-JU-42STEocApDgxg9VM0vfrzI_ednV9lP7AIgjON-VxP1Uq8TPhGkyimzyWOnKqb3Nc-gs2zt20XW8KPv4Z2Q4qORlOtoOceWNPzDLHxdi6KaaCAARLaO3FpwgvNSTRgzNoe1xrMfbP9baE1mYS95ek7ytEUPk22p4dJWD7UE8xLZ4Tx_7nkyRQ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;391&quot; data-original-width=&quot;1049&quot; height=&quot;119&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjUql-JU-42STEocApDgxg9VM0vfrzI_ednV9lP7AIgjON-VxP1Uq8TPhGkyimzyWOnKqb3Nc-gs2zt20XW8KPv4Z2Q4qORlOtoOceWNPzDLHxdi6KaaCAARLaO3FpwgvNSTRgzNoe1xrMfbP9baE1mYS95ek7ytEUPk22p4dJWD7UE8xLZ4Tx_7nkyRQ&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;It&#39;s checking to see if Windows is new enough to have a function &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;GetLogicalProcessorInformation&lt;/span&gt;&lt;/a&gt; and calling it if it does. We don&#39;t really want the game to call this function since it ruins our lie about the number of processors. Fortunately, this function is smart enough to handle the situation where that function doesn&#39;t exist. So to prevent it from calling this function, we&#39;ll just change the name to all X&#39;s. It will attempt to find a function called XXXXXX... which obviously doesn&#39;t exist. IDA can also confirm for us that this is the only location where that string is used, so we can safely change the name.&lt;/p&gt;&lt;p&gt;Unfortunately we&#39;re not out of the woods yet. There is one more function, and it&#39;s a doozy:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjlpmqPDw7gy26H_8_QoGnXiDGWEWABwEIWqKsrOxnWGCVn0GznXoBYu2TnnmZ8iXLLhTkVMtXmrsK6Xtc3smzpMs5iZVeM9f-H9n8HObQNf5lccSdnrcgT4T2avxK0DBecIuPUlYcJSPlUgL9vRqi6E4UOBjv--y_nLY6_hnBxnWE6B12fx96bQ9WGeg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;328&quot; data-original-width=&quot;877&quot; height=&quot;120&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjlpmqPDw7gy26H_8_QoGnXiDGWEWABwEIWqKsrOxnWGCVn0GznXoBYu2TnnmZ8iXLLhTkVMtXmrsK6Xtc3smzpMs5iZVeM9f-H9n8HObQNf5lccSdnrcgT4T2avxK0DBecIuPUlYcJSPlUgL9vRqi6E4UOBjv--y_nLY6_hnBxnWE6B12fx96bQ9WGeg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;This function is doing a &lt;a href=&quot;https://www.felixcloutier.com/x86/idiv&quot; target=&quot;_blank&quot;&gt;signed integer divide&lt;/a&gt; by the number of processors. None of our tricks thus far will work for this. Let&#39;s take a deeper look at what this instruction is doing. It takes a 64-bit value with the most significant 32 bits in &lt;i&gt;edx&lt;/i&gt; and the least significant 32 bits in &lt;i&gt;eax&lt;/i&gt; and divides by a 32-bit value (in this case, our &lt;span style=&quot;font-family: courier;&quot;&gt;dwNumberOfProcessors&lt;/span&gt;)--&lt;i&gt;eax&lt;/i&gt; will receive the quotient and &lt;i&gt;edx&lt;/i&gt; will contain the remainder. It does this in only 4 bytes!&lt;/p&gt;&lt;p&gt;However, it doesn&#39;t appear to be using the quotient at all, just the remainder. So this is actually doing a modulus by the number of processors. So what we need to do is actually a modulus by our lie of 8 processors. Can we do it in 4 bytes!?&lt;/p&gt;&lt;p&gt;Well, we have a few things going for us. The original function does a &lt;a href=&quot;https://www.felixcloutier.com/x86/cwd:cdq:cqo&quot; target=&quot;_blank&quot;&gt;&lt;i&gt;cdq&lt;/i&gt;&lt;/a&gt;&amp;nbsp;(convert doubleword to quadword) instruction to set up for the divide, it also needs &lt;i&gt;eax&lt;/i&gt; and &lt;i&gt;edx&lt;/i&gt; for the divide but only &lt;i&gt;edx&lt;/i&gt; with the result. And since we&#39;ve chosen a magic power-of-two value of 8, we can achieve a modulus by doing an &lt;i&gt;AND&lt;/i&gt; operation with (8 - 1) or 7.&lt;/p&gt;&lt;p&gt;Within reason, we can also move some instructions around as long as the side-effects are the same.&lt;/p&gt;&lt;p&gt;All we really need to do is this, which is only 5 bytes:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiItedDJgbKbIWpBb8ytqbAifdPwNjmoFqSMxQ6DktBmW1Xrfuw1wlFwJ1Ceo--7Q4f_WnhrlxOAghtNZ8lUl6kpLwevbtWmLKJjbObXad7cioM-rLzFfziG24hyvzgn0zPtQWtTm3oM4AjpmnzRxroXXlQRAjNlvc_EoynSlfUnFi9M8A1MWXLM8nvfw&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;501&quot; data-original-width=&quot;889&quot; height=&quot;180&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiItedDJgbKbIWpBb8ytqbAifdPwNjmoFqSMxQ6DktBmW1Xrfuw1wlFwJ1Ceo--7Q4f_WnhrlxOAghtNZ8lUl6kpLwevbtWmLKJjbObXad7cioM-rLzFfziG24hyvzgn0zPtQWtTm3oM4AjpmnzRxroXXlQRAjNlvc_EoynSlfUnFi9M8A1MWXLM8nvfw&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;And we&#39;ve already established that we don&#39;t need the &lt;i&gt;cdq&lt;/i&gt; instruction since that was part of the divide. So &lt;i&gt;cdq&lt;/i&gt; + &lt;i&gt;idiv&lt;/i&gt; gives us 5 bytes! All we need to do is move the &quot;&lt;span style=&quot;font-family: courier;&quot;&gt;mov r8, rbx&lt;/span&gt;&quot; one byte earlier then we have the 5 bytes that we need together!&lt;/p&gt;&lt;p&gt;That&#39;s all the locations calling &lt;span style=&quot;font-family: courier;&quot;&gt;GetSystemInfo&lt;/span&gt; patched up. The question is, does it run?&lt;/p&gt;&lt;p&gt;I fired up the game, and it purrs along like a charm! No more stalls. Seems like the theory of my computer having too many CPUs for an older game trying to take advantage of them was accurate.&lt;/p&gt;&lt;p&gt;I&#39;ve uploaded the patched executable to &lt;a href=&quot;https://www.nexusmods.com/assassinscreedrogue/mods/255&quot; target=&quot;_blank&quot;&gt;Nexus Mods&lt;/a&gt;&amp;nbsp;but as of this writing there haven&#39;t been any downloads. I guess not many people are trying to play a 10-year-old game with 64+ CPUs! At least I&#39;m enjoying it again 😉&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjzI-L2hbdu2QzU5IVsJivBRrGmIo-Kn9zKe1uRKfgOHmdpbO8oDd5cOIH0kWXfAMGHwj0oOacCEioEy3AnhHY08SV7GHtEEqRTDBix94odalUl_H9lhrRBhC6maWTe3sdXwmA4scSANSxsgHOYVbkOyY-LiYoVkEK311XsNHdVTX09oZgz8czQVz7HSQ&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;953&quot; data-original-width=&quot;2621&quot; height=&quot;116&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjzI-L2hbdu2QzU5IVsJivBRrGmIo-Kn9zKe1uRKfgOHmdpbO8oDd5cOIH0kWXfAMGHwj0oOacCEioEy3AnhHY08SV7GHtEEqRTDBix94odalUl_H9lhrRBhC6maWTe3sdXwmA4scSANSxsgHOYVbkOyY-LiYoVkEK311XsNHdVTX09oZgz8czQVz7HSQ&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;</description><link>http://blog.joshuakriegshauser.com/2024/10/game-hacking.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRhoT3Xfiz1f7xlT0hEwJ5uJjIErgnzxvepjsRxzjuzMNhNSZAIMkpg97Sg7r3sLT867X0XIzp2NHU0F9-qTUGkqmD5iAOP_C0DJP742iI1fcm1jSnPlvY4bNaQwJ19tWbB2-Jr5PBjjnWKBpN0lDxlTAOxBjLyyHkEPYvUsVufU-S31ZwptJSlrktPQ/s72-c/Capture.PNG" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-8787755162202790507</guid><pubDate>Sat, 06 Jul 2024 20:48:00 +0000</pubDate><atom:updated>2024-07-06T13:48:15.212-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">github</category><category domain="http://www.blogger.com/atom/ns#">hacking</category><category domain="http://www.blogger.com/atom/ns#">libraries</category><category domain="http://www.blogger.com/atom/ns#">nvidia</category><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">threading</category><category domain="http://www.blogger.com/atom/ns#">work</category><title>Fixing a Memory Corruption bug in Python</title><description>&lt;p&gt;For &lt;a href=&quot;https://www.nvidia.com/en-us/omniverse/&quot; target=&quot;_blank&quot;&gt;Omniverse&lt;/a&gt;&amp;nbsp;we use Python for scripting. About a year and a half ago, we upgraded from Python 3.7 to Python 3.10. Shortly thereafter, we started seeing occasional memory corruption issues crop up, but it took a while to actually link the cause to Python.&lt;/p&gt;&lt;p&gt;A lot of the crashes that we were seeing involved very deep and complex Python stack traces in seemingly innocuous things: the garbage collector, releasing a reference, sometimes allocating new memory within Python. Since my team in the Omniverse group handles the plugin system, we manage the plugin that embeds Python into Omniverse, which means I was assigned all of these crashes for triage.&lt;/p&gt;&lt;p&gt;We do have some internal Python experts, and my team wasn&#39;t the tip of the spear on the 3.7 -&amp;gt; 3.10 upgrade (though we were involved). The nature of memory corruption means that crashes can happen in very random ways, since any call to acquire or free memory (which happens a LOT) may trigger a crash. So we had 20+ different stack traces that were considered to be part of the same memory corruption pathology. Curiously, this was only happening on Windows, and another clue was that quite often we would see an error in the log sometimes well before the crash occurred:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;RuntimeError: &amp;lt;_overlapped.Overlapped object at 0xXXXXXXXXXXXXXXXX&amp;gt; still has pending operation at deallocation, the process may crash.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&quot;Helpful!&quot; I thought to myself sarcastically. Well, the process &lt;i&gt;was &lt;/i&gt;in fact crashing, so this may be an indication as to what happening.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;However, this crash was fairly low priority. It wasn&#39;t happening &lt;i&gt;often &lt;/i&gt;per se, but it was annoying. I made a task to track all of the different automatically generated crash stack traces, and periodically tried to find someone who could look into it. Since it was Windows-only and rare, it never percolated up to the top of anyone&#39;s priority queue.&lt;/p&gt;&lt;p&gt;Months passed and I continued to be irritated when another new stack trace would pop up. Everyone, including me, was busier with higher priority tasks. Since no one else could be bothered to fix it, I eventually decided to take a crack at it.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&quot;Overlapped&quot; I/O?&lt;/h3&gt;&lt;div&gt;Typically when you&#39;re writing a program and you want to do a network request, or reading a file, or any other sort of input/output the &lt;i&gt;easiest &lt;/i&gt;thing to do is to stop everything and wait because you need the results before continuing. But in the world of 60+ FPS, stopping and waiting for even a few milliseconds can feel sluggish. We instead want to do things&amp;nbsp;&lt;i&gt;a-synchronously&lt;/i&gt;--have them happen in a background process and the program can do other things while waiting. One way of doing this is through &lt;i&gt;parallelization&lt;/i&gt;, that is, using a separate thread or CPU to do work, but this can also be messy. Also, Windows is old and computers used to just have one CPU, so a long time ago Windows started doing a-synchronicity through &lt;i&gt;concurrency &lt;/i&gt;and the Operating System would handle interleaving all of the requests. &quot;Overlapped&quot; I/O is how Windows started doing that: multiple I/O operations could be done at a time, hence they overlap each other.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The problem is, it&#39;s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/sync/synchronization-and-overlapped-input-and-output&quot; target=&quot;_blank&quot;&gt;pretty complicated&lt;/a&gt;. And even more so in an environment like Python where implementation requires both C code and Python code.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Reproducing the problem&lt;/h3&gt;&lt;div&gt;The first step was to try to reduce the problem to a minimum so that it was easier to debug. I also knew that we started having this problem when we moved from Python 3.7 to 3.10, so I looked at the changes to see what might be different. I noticed this in the &quot;&lt;a href=&quot;https://docs.python.org/3/whatsnew/3.8.html&quot; target=&quot;_blank&quot;&gt;what&#39;s new in Python 3.8&lt;/a&gt;&quot; article:&lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi4ATPhNO7_4Eix2U9ifxz9NBStVSGBiO4VR8Sr9MH40eRCzeqX5ATnEbGLReiUE-YNOTtbbKzHP8dx_kMMeZ2OijDRB_NVrJXtL_3oak6Ur6dc1cPjHgGMLAtaGk_9dr9cMrfVBIMneIJ79mJRwhXQICMCy1uNbAh1DXe2YzPYXEHpZyKk3RemIj4AxA&quot; style=&quot;clear: left; margin-bottom: 1em;&quot;&gt;&lt;img alt=&quot;On Windows, the default event loop is now ProactorEventLoop. (Contributed by Victor Stinner in bpo-34687.)&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;39&quot; data-original-width=&quot;806&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi4ATPhNO7_4Eix2U9ifxz9NBStVSGBiO4VR8Sr9MH40eRCzeqX5ATnEbGLReiUE-YNOTtbbKzHP8dx_kMMeZ2OijDRB_NVrJXtL_3oak6Ur6dc1cPjHgGMLAtaGk_9dr9cMrfVBIMneIJ79mJRwhXQICMCy1uNbAh1DXe2YzPYXEHpZyKk3RemIj4AxA=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;It turns out that Windows and Linux asyncio event loops are implemented quite differently, which makes sense because Asynchronous IO works quite differently on Windows and Linux. With Python 3.8, asyncio now used Overlapped IO by default through the &lt;a href=&quot;https://en.wikipedia.org/wiki/Proactor_pattern&quot; target=&quot;_blank&quot;&gt;Proactor&lt;/a&gt; event loop.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Omniverse was also doing something different from most Python applications: constantly stopping and re-starting the event loop. See, in most Python applications the program hands control to asyncio until the program finishes; asyncio then dispatches everything that the application does. Since Omniverse uses embedded Python, we still have a graphical application that needs to run, so each frame of the graphical application we would just run the asyncio event loop for one &quot;tick&quot; and then stop it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I wrote a simple app that emulated that behavior, but I couldn&#39;t get it to crash, so that wasn&#39;t enough to do it. We had a sample case that could produce the Overlapped error message typically within 5 minutes (which is an &lt;i&gt;eternity &lt;/i&gt;when you&#39;re waiting for it), so I looked at what it was doing. After much debugging I found another clue: other threads were calling &lt;span style=&quot;font-family: courier;&quot;&gt;call_soon_threadsafe&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;on the event loop. I added that to my test program. BINGO. This program had an 80% chance to print the error message within 60 seconds:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEih7kIvmKyvgzUXjW5Dqqp08XxZY11GLuQjqFP6qSRsBGKx1li5XooA5gYXzt1Y0FBU3_yAuQMDHc5GR6IipI7HPH69vdh6mcSqaKqned_DUDeVrD-Hj4xwxy_kK0PDSMx89ZeK78IbUPtBE8VVPsaf-UfM1i4MHKlKsBsA9o0E_3VCiztNT7KKsQPtwg&quot; style=&quot;clear: left; margin-bottom: 1em;&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;338&quot; data-original-width=&quot;423&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEih7kIvmKyvgzUXjW5Dqqp08XxZY11GLuQjqFP6qSRsBGKx1li5XooA5gYXzt1Y0FBU3_yAuQMDHc5GR6IipI7HPH69vdh6mcSqaKqned_DUDeVrD-Hj4xwxy_kK0PDSMx89ZeK78IbUPtBE8VVPsaf-UfM1i4MHKlKsBsA9o0E_3VCiztNT7KKsQPtwg=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;This program starts a thread that pings away scheduling a dummy function to run on the next loop iteration. It&#39;s real purpose is to send a wakeup signal to the event loop. The main thread just constantly does our stop/run idiom like Omniverse does so it doesn&#39;t have to hand control to the event loop permanently.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h3&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;But does it corrupt memory?&lt;/span&gt;&lt;/h3&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;The error message comes from the implementation of _overlapped, which is done in C and acts as a bridge between Python code for the asyncio event loop and the Windows API call to manage overlapped I/O. Let&#39;s look at what happens before that.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;When the &lt;/span&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;run_forever&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;call is cleaning up it tries to cancel the &quot;_self_reading_future&quot;:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; font-family: inherit; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiH2o4jwKr3XTQ-AM_OXJb2EpQVGaDcGbIa1BP-9_IYRkKsgODZ0KNUdENhI0yNqgMZl6vuIHjLipKccFnuPzwh1xaOWndDbmtXSUm19M6t3fD7geKH_ggJuyv4eEd2U_PlO0WEEElReNa73kZAjfGtyYk6FQAgPYLHoJ_M99zZ92qgFsoiGlzssXYgMA&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;353&quot; data-original-width=&quot;609&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiH2o4jwKr3XTQ-AM_OXJb2EpQVGaDcGbIa1BP-9_IYRkKsgODZ0KNUdENhI0yNqgMZl6vuIHjLipKccFnuPzwh1xaOWndDbmtXSUm19M6t3fD7geKH_ggJuyv4eEd2U_PlO0WEEElReNa73kZAjfGtyYk6FQAgPYLHoJ_M99zZ92qgFsoiGlzssXYgMA=s16000&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; font-family: inherit; text-align: left;&quot;&gt;Which in turn calls into the C code for _overlapped and invokes a Windows API function called &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/fileio/cancelioex-func&quot; target=&quot;_blank&quot;&gt;CancelIoEx&lt;/a&gt;:&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; font-family: inherit; text-align: left;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjWvTV3-YNKKlFhTqNtc5AS0XkCof9ZOk12xmyAjZn-aw28bxwSgifB7KJQGBRT4DyA30xDIXNqkW-GrwK1L99BNw6MZ8cdZB8EI3p9zGhgcCxp_vvjI2M52Y4U0gsCRxqEWpqHq52xxVobgfYiaH_YBklmAsn_5vRstUM_XtdNrcRfHtg2BwnLs0Y68g&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;462&quot; data-original-width=&quot;630&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjWvTV3-YNKKlFhTqNtc5AS0XkCof9ZOk12xmyAjZn-aw28bxwSgifB7KJQGBRT4DyA30xDIXNqkW-GrwK1L99BNw6MZ8cdZB8EI3p9zGhgcCxp_vvjI2M52Y4U0gsCRxqEWpqHq52xxVobgfYiaH_YBklmAsn_5vRstUM_XtdNrcRfHtg2BwnLs0Y68g=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Then the &lt;/span&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;run_forever&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;function unregisters the &#39;overlapped&#39; object (ov) and sets the future to None, effectively destroying the object.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;b&gt;This is where the danger arises&lt;/b&gt;! The overlapped structure (managed by the overlapped Python object) has been passed to the Windows API when an IO operation was requested (in this case it&#39;s for the &lt;a href=&quot;https://docs.python.org/3/library/socket.html#socket.socketpair&quot; target=&quot;_blank&quot;&gt;socketpair&lt;/a&gt;&amp;nbsp;used to notify the event loop for other threads) and &lt;b&gt;cannot be deallocated &lt;/b&gt;until Windows reports that it&#39;s done with it!&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;The deallocation function attempts to check this, and reports the error message if Windows isn&#39;t done with it. But then goes on to deallocate the structure anyways, which can lead to memory corruption!&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjJw56qQZAv0DK13LNwQsvLV5pVm8Kel2sNGjMrM66BeLS33_uH6vhHYsIUl37X_el08njrQDsS5T5zgDQB9pp-Hg6FmVpXSgRxmDwZ8HpnDzj7zMYG63tC-J4Ujojyx4ptxYPfCyfLySUTpJki7tvXg7S3NweEs9c_wxmmVEWbE1znESgq0LkFd6OeOQ&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;844&quot; data-original-width=&quot;606&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjJw56qQZAv0DK13LNwQsvLV5pVm8Kel2sNGjMrM66BeLS33_uH6vhHYsIUl37X_el08njrQDsS5T5zgDQB9pp-Hg6FmVpXSgRxmDwZ8HpnDzj7zMYG63tC-J4Ujojyx4ptxYPfCyfLySUTpJki7tvXg7S3NweEs9c_wxmmVEWbE1znESgq0LkFd6OeOQ=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;While not ideal, a better alternative might be to report the error and &lt;i&gt;leak &lt;/i&gt;the OVERLAPPED structure so that a crash won&#39;t occur. But the &lt;i&gt;best &lt;/i&gt;alternative is to see if we can fix the problem.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;With a reproducible case and an understanding of what was going on, I filed a &lt;a href=&quot;https://github.com/python/cpython/issues/116773&quot; target=&quot;_blank&quot;&gt;Python issue&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;h3 style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style=&quot;clear: both; text-align: left;&quot;&gt;Can we fix it?&lt;/h3&gt;&lt;div&gt;I put the initial diagnosis into the Python issue: CancelIoEx is being properly called, returns TRUE meaning that Windows requested the cancellation (NOT that it was cancelled yet!), but the deallocation function sees that the overlapped I/O hasn&#39;t yet completed. It tries to call CancelIoEx again (this always fails with ERROR_NOT_FOUND since the previous cancel request succeeded), and then calls &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult&quot; target=&quot;_blank&quot;&gt;GetOverlappedResult&lt;/a&gt;&amp;nbsp;(note that wait=false because the second CancelIoEx attempt fails), which will &lt;i&gt;also &lt;/i&gt;fail with ERROR_IO_PENDING. As a test I tried changing it to wait, but this just caused a hang.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What gives? Originally I though this was a bug in Windows and handling Overlapped I/O, but then I tried something else. Not only is this system using Overlapped I/O, it&#39;s using another complex Windows-ism: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports&quot; target=&quot;_blank&quot;&gt;I/O Completion Ports&lt;/a&gt;. This system is a way to group multiple I/O requests together, so that instead of having to check myriad different requests, you check one &quot;completion port&quot; to find out when any of your requests finish. What if the cancelled I/O request is stuck in the I/O completion port? Instead of waiting on GetOverlappedResult, maybe I need to read the request from &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus&quot; target=&quot;_blank&quot;&gt;GetQueuedCompletionStatus&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;BINGO.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Turns out that was the case: it really depends on where within the labyrinthine Windows I/O ecosystem the I/O request ended up before the cancel request happened.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So now we could make an &quot;official&quot; fix recommendation. Generally large projects like Python desire fix recommendations from outsiders like me to be as simple and elegant as possible. Which meant that I would have better luck without completely trying to change how their Overlapped I/O worked. Since all we needed to do was make sure that GetQueuedCompletionStatus was called on our _overlapped object again, that gave us a goal. GetQueuedCompletionStatus was actually called every time the event loop ran, and it already cleaned up finished _overlapped objects once they completed. All we need to do is only allow cleanup after canceling &lt;i&gt;if &lt;/i&gt;the I/O was complete, otherwise it would get cleaned up automatically during the next event loop iteration.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So that just meant changing the line&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj8rlNoaZ6TLREnnppskazduPfDfOz_ZI8uLn6bw6g_vll2LjuLA0BJWftJM4FnOzVvElFNbeMrq6C0LeUI_lncwm9Z1aK56FGct9fLwi8jbs3nCfXAZdk84bv5EM4g1R02Xg2mG5CeQpY04adTJirIqgm6qvk-aC8nJSPk5YVotYRq3RcYJTTnV5ILlg&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;22&quot; data-original-width=&quot;146&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj8rlNoaZ6TLREnnppskazduPfDfOz_ZI8uLn6bw6g_vll2LjuLA0BJWftJM4FnOzVvElFNbeMrq6C0LeUI_lncwm9Z1aK56FGct9fLwi8jbs3nCfXAZdk84bv5EM4g1R02Xg2mG5CeQpY04adTJirIqgm6qvk-aC8nJSPk5YVotYRq3RcYJTTnV5ILlg=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;to&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhJtljKi2P1tFWwU_s0EPVXE0Ms0SXC4lP7nGqW6MtHH7EEwZPE-6H4f9se3kQzxXthhHUaboLil7zhJWW_kQSLT6MizU-Ki4nfttzzinIoL2HaNqkLQzU4PLerCFhNuMyHSr8yaesES-LHIcy1pIS1-e2AXl46YhPLUaTCfxw8AtmWzQS2GhEXqAxDxg&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; class=&quot;hoverZoomLink&quot; data-original-height=&quot;21&quot; data-original-width=&quot;289&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhJtljKi2P1tFWwU_s0EPVXE0Ms0SXC4lP7nGqW6MtHH7EEwZPE-6H4f9se3kQzxXthhHUaboLil7zhJWW_kQSLT6MizU-Ki4nfttzzinIoL2HaNqkLQzU4PLerCFhNuMyHSr8yaesES-LHIcy1pIS1-e2AXl46YhPLUaTCfxw8AtmWzQS2GhEXqAxDxg=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Done and dusted!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Of course, for the &lt;a href=&quot;https://github.com/python/cpython/pull/116774&quot; target=&quot;_blank&quot;&gt;official fix&lt;/a&gt; I added a unit test and a bunch of comments since Overlapped I/O and I/O Completion Ports are frustratingly touchy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As a bonus, &lt;a href=&quot;https://en.wikipedia.org/wiki/Guido_van_Rossum&quot; target=&quot;_blank&quot;&gt;Guido van Rossum&lt;/a&gt;&amp;nbsp;(creator of Python) commented positively on the fix, and eventually we got the fix merged back as far as Python 3.8--the oldest supported version (for security fixes), but also the first version where the ProactorEventLoop became the default.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-bgimage=&quot;&quot; id=&quot;hzViewer&quot; style=&quot;--darkreader-inline-bgcolor: initial; --darkreader-inline-bgimage: none; background: none; cursor: pointer; display: none; height: auto; left: 5px; line-height: 0px; opacity: 1; overflow: hidden; padding: 10px 5px; pointer-events: none; position: absolute; top: 961px; visibility: hidden; width: auto; z-index: 2147483647;&quot;&gt;&lt;/div&gt;&lt;div data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-bgimage=&quot;&quot; id=&quot;hzViewer&quot; style=&quot;--darkreader-inline-bgcolor: initial; --darkreader-inline-bgimage: none; background: none; cursor: pointer; display: none; height: auto; left: 44px; line-height: 0px; opacity: 1; overflow: hidden; padding: 10px 5px; pointer-events: none; position: absolute; top: 2055px; visibility: visible; width: auto; z-index: 2147483647;&quot;&gt;&lt;/div&gt;</description><link>http://blog.joshuakriegshauser.com/2024/07/fixing-memory-corruption-bug-in-python.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEi4ATPhNO7_4Eix2U9ifxz9NBStVSGBiO4VR8Sr9MH40eRCzeqX5ATnEbGLReiUE-YNOTtbbKzHP8dx_kMMeZ2OijDRB_NVrJXtL_3oak6Ur6dc1cPjHgGMLAtaGk_9dr9cMrfVBIMneIJ79mJRwhXQICMCy1uNbAh1DXe2YzPYXEHpZyKk3RemIj4AxA=s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3175699066101606152</guid><pubDate>Wed, 05 Jun 2024 15:19:00 +0000</pubDate><atom:updated>2024-06-05T08:37:34.337-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">libraries</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">nvidia</category><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">threading</category><category domain="http://www.blogger.com/atom/ns#">work</category><title>Basic Callback Hygiene</title><description>&lt;p&gt;There&#39;s a tenet that I&#39;ve been pushing on for a few years now from my little corner of NVIDIA, and that is the concept of &lt;i&gt;Basic Callback Hygiene&lt;/i&gt;.&lt;/p&gt;&lt;p&gt;For the uninitiated, a &lt;i&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Callback_(computer_programming)&quot; target=&quot;_blank&quot;&gt;callback&lt;/a&gt;&amp;nbsp;&lt;/i&gt;simply-put is a function that I can pass to another function that it will later call to &quot;call me back.&quot; A lot of subscription services in programming work like this: Something is going to happen in the future. I want to be notified of it, so I&#39;ll call a subscription function and give it a function that it can call to tell me when that happens.&lt;/p&gt;&lt;p&gt;This simple concept gets a lot more complicated when you ask what sorts of things you can do within that callback function. What happens when I don&#39;t care about the subscription anymore? What if the module containing the function unloads? What thread is the callback going to happen on? And so on, and so forth.&lt;/p&gt;&lt;p&gt;I&#39;ve experienced problems with all of these complications over the course of my career, and given the amount of asynchronous and modular code that I&#39;ve been writing for Omniverse, I finally distilled down a simple set of rules:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Callbacks can ask to be unregistered from within the callback.&lt;/li&gt;&lt;li&gt;Strong recommendation to not hold &lt;a href=&quot;https://en.wikipedia.org/wiki/Lock_(computer_science)&quot; target=&quot;_blank&quot;&gt;locks&lt;/a&gt; (i.e. a mutex) while calling callbacks.&lt;/li&gt;&lt;li&gt;When a callback asks to be unregistered, there must be a strong guarantee that the callback is not currently being called, nor will ever be called in the future.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;That&#39;s it. A subscription system that follows these basic principles will avoid 99% of the issues that we typically run into with callbacks.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let&#39;s examine each of these. To put this in practical terms, let&#39;s say that we&#39;re designing an event system. We can &lt;i&gt;subscribe &lt;/i&gt;to an event which will register our callback and we can &lt;i&gt;dispatch &lt;/i&gt;an event which will callback all of the subscribers from the context of the dispatching thread (so basically it loops over all subscribers and calls their callback). Our imaginary system is thread-safe, so we can &lt;i&gt;subscribe &lt;/i&gt;or &lt;i&gt;dispatch&lt;/i&gt;&amp;nbsp;from any thread. We can also &lt;i&gt;unsubscribe&lt;/i&gt;&amp;nbsp;to unregister our subscription.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Unregistration from within a callback&lt;/h3&gt;&lt;div&gt;This is probably the most useful one. Let&#39;s consider the case where I &lt;i&gt;cannot &lt;/i&gt;unsubscribe from the callback but I want to. My callback is called and I can handle the event, but I cannot unsubscribe. This particular callback only wants to receive the first event after registration and no more events (an example of this might be a one-shot function that I want to defer until the next frame).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since I cannot unsubscribe, I must be able to conditionally have a function run later that will then unsubscribe my function outside of the event, and I need to ignore any more events that I might receive in the meantime.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This makes something that should be simple much more troublesome, and every place that I want to do this sort of action now requires more thought, effort, time, testing, etc. Changing the system to allow un-subscription from within the callback means that the complexity of handling that situation is in a singular place: my event system, rather than each and every callback that needs that behavior.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Don&#39;t Hold Locks&lt;/h3&gt;&lt;div&gt;Holding a lock while calling a callback is dangerous because the callback can execute arbitrary code. The callback may call back into our event system and we don&#39;t want the complication of having recursive locks (which are &lt;a href=&quot;https://rlc.vlinder.ca/blog/2009/07/recursive-locking-is-evil-or-is-it/&quot; target=&quot;_blank&quot;&gt;arguably evil&lt;/a&gt;) or contributing to lock cycles which can cause &lt;a href=&quot;https://en.wikipedia.org/wiki/Deadlock&quot; target=&quot;_blank&quot;&gt;deadlocks&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Writing an event system to be thread-safe and yet not hold any locks while calling callbacks is challenging to say the least. Generally this requires something akin to tracking the number of threads that are in the middle of doing callbacks, at a minimum. We typically do it by adding a thread identifier to a list while holding a lock, and then unlocking to call the callback, then re-acquiring the lock and removing the thread identifier from the list. This is more complicated to be sure, and the reason why will be evident in the next section. There are other ways to do it, but you&#39;ll need some tracking to maintain state while the mutex is unlocked for the callback.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This also means that how you walk the list of subscriptions must be stateful in a way that can resume after the mutex is unlocked and re-acquired. If these subscriptions are stored in C++&#39;s&amp;nbsp;&lt;span style=&quot;font-family: courier;&quot;&gt;std::vector&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;for instance, there exists a possibility that unlocking the mutex will allow another thread to subscribe, which may resize and therefore reallocate the vector, which invalidates all iterators; or another thread may unsubscribe which will remove an entry, which can affect the position of the current thread walking over the vector. (Solutions to these, respectively, might involve using indexes instead of iterators, and leaving null pointers in the array to mark empty spaces).&lt;/span&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Strong Unregistration Guarantees&lt;/h3&gt;&lt;div&gt;This is by far the hardest issue to solve correctly. Here&#39;s a real-world scenario: right before a module unloads (in the main thread, say), it tells our Event system to unsubscribe a previously-subscribed callback. Without a strong guarantee, what will happen if another thread is currently calling our subscribed callback? As noted above, we don&#39;t want to be holding locks while calling callbacks, so waiting on a lock isn&#39;t going to wait for our callback to finish. What has happened in actual code is that the unsubscribe happens quickly, the module proceeds to unload, and then a crash occurs because another thread was still executing a function that was in the module. Or after unloading, objects were destructed since we would no longer get events, but with another thread in our callback function, a crash occurs because deleted memory was accessed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Or the problem that I&#39;m fixing right now: memory corruption occurs because a dependent structure was freed, but a race with the callback means that we were still changing things inside the structure.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It quickly becomes apparent that any request to unregister must wait for any other calls to finish. Also, once we return from the unregister function, it stands to reason that we cannot ever call the callback again. To do so would be violating the caller&#39;s unregistration request.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This then requires a strong ordering guarantee:&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Unregister function is called.&lt;/li&gt;&lt;ol&gt;&lt;li&gt;WAIT for any calls to the callback in question to finish*.&lt;/li&gt;&lt;li&gt;PREVENT any future calls to the callback.&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Return from the unregister function. At this point the caller can guarantee that our event system is free of the callback and it is safe to destroy structures.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&quot;Ah,&quot; the astute and observant amongst you might notice, &quot;what if the unregistration is from within the callback?!&quot; This was our first point, above. If we WAIT for any calls to the callback to finish and the unregistration call is coming from the callback, we would wait forever. So we amend the line with the asterisk (*) above instead to say: &quot;WAIT for any calls to the callback in question in other threads to finish.&quot;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So our subscription process is pretty simple:&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Take the lock&lt;/li&gt;&lt;li&gt;Append our subscription information to the &lt;span style=&quot;font-family: courier;&quot;&gt;std::vector&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;(or similar)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Unlock the lock&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;The notification process is a bit more complicated as it has to synchronize with the unsubscribe process:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Take the lock&lt;/li&gt;&lt;li&gt;Iterate over the &lt;span style=&quot;font-family: courier;&quot;&gt;std::vector&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;(or similar) by index, skipping null entries. For each:&lt;/span&gt;&lt;/li&gt;&lt;ol&gt;&lt;li&gt;Add the current thread identifier to a list of threads that are notifying.&lt;/li&gt;&lt;li&gt;Unlock the lock&lt;/li&gt;&lt;li&gt;Call the callback function&lt;/li&gt;&lt;li&gt;Re-acquire the lock. At this point, the vector may have been reallocated and grown, and subsequent entries may have been unregistered. Any other thread could have done anything.&lt;/li&gt;&lt;li&gt;Remove our current thread identifier from the list of threads that are notifying.&lt;/li&gt;&lt;li&gt;Is a waiting flag set on the subscription? If so, notify the waiting thread (i.e. using a condition variable).&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Unlock the lock&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;And now, the &lt;i&gt;pièce de résistance&lt;/i&gt;: the unsubscribe process:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Take the lock&lt;/li&gt;&lt;li&gt;Find the subscription to remove. If not found, unlock and return; otherwise:&lt;/li&gt;&lt;li&gt;Set the entry in the &lt;span style=&quot;font-family: courier;&quot;&gt;std::vector&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;to null, but it is not destroyed yet!&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Is the subscription currently being notified by a thread other than the current thread? If so, set a waiting flag on the subscription that is used in 2.6 above.&lt;/li&gt;&lt;li&gt;Unlock the lock.&lt;/li&gt;&lt;li&gt;If the waiting flag was set, wait on the condition variable for notification from 2.6 above. Otherwise it is safe to destroy the subscription and return.&lt;/li&gt;&lt;li&gt;Re-acquire the lock.&lt;/li&gt;&lt;li&gt;If the list of notifying threads contains threads besides the current thread go to step 5.&lt;/li&gt;&lt;li&gt;Unlock the lock. It is now safe to destroy the subscription.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;This will allow our unsubscribe to wait until no threads are notifying our subscription, or only our thread that is currently unsubscribing during notification.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Of course, there is a lot more nuance to this, especially when it comes to handling recursion (dispatching within a callback) or even just the complexity of working with multiple threads, but this should help you understand the concept of &lt;i&gt;Basic Callback Hygiene&lt;/i&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;</description><link>http://blog.joshuakriegshauser.com/2024/06/basic-callback-hygiene.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-6916799415634284640</guid><pubDate>Wed, 05 Jul 2017 00:42:00 +0000</pubDate><atom:updated>2017-07-05T15:19:11.336-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">daybreak</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">kotk</category><category domain="http://www.blogger.com/atom/ns#">planetside2</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Resolute Timers</title><description>This weekend I got linked into a tweetstorm regarding a tool called &lt;a href=&quot;http://www.lucashale.com/timer-resolution/&quot;&gt;Set Timer Resolution&lt;/a&gt; by one &lt;a href=&quot;http://lucashale.com/&quot;&gt;Lucas Hale&lt;/a&gt;. People are claiming that using this tool results in better hit accuracy, faster responsiveness and higher frame-rates for &lt;a href=&quot;https://www.h1z1.com/king-of-the-kill/home&quot;&gt;King of the Kill&lt;/a&gt; and other &lt;a href=&quot;https://www.daybreakgames.com/home&quot;&gt;Daybreak&lt;/a&gt; titles.&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://cms.lucashale.com/wp-content/uploads/2010/08/TimerRes.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;257&quot; data-original-width=&quot;482&quot; src=&quot;https://cms.lucashale.com/wp-content/uploads/2010/08/TimerRes.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Seen here, in all its majesty&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
It looks like other people have said that this helps, and at one point it looks like the download link was removed from the site. At the time of this writing, &lt;a href=&quot;http://www.download3k.com/Install-Lucas-Hale-TimerResolution.html&quot;&gt;Download 3K is apparently a valid mirror&lt;/a&gt;&amp;nbsp;(MD5:&amp;nbsp;&lt;span style=&quot;background-color: #fafaff; color: #222222; font-family: &amp;quot;arial&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;geneva&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;4b3bccdb3bcbd48162aa77270d910276&lt;/span&gt;). I cannot recommend using any specific third-party applications, including this one. Your mileage may vary and incorrect use of software may cause issues.&lt;br /&gt;
&lt;br /&gt;
This specific very simple app (only 32k in size!) does not affect the game in any way shape or form. In fact, it was originally authored back in 2007, way before King of the Kill. Instead, it tells Windows to check it&#39;s timers more often. That&#39;s it.&lt;br /&gt;
&lt;br /&gt;
Imagine this. You need to do something in 30 seconds, but you only have a clock with a minute hand. You glance at the clock and it says 4:59 pm. Once it changes to 5:00 pm, has 30 seconds elapsed? Not necessarily! What if you glanced at the clock a mere second before it changed? To ensure that a full 30 seconds has elapsed you would actually have to wait until 5:01 pm to &lt;i&gt;guarantee &lt;/i&gt;that at least 30 seconds has passed, but &lt;i&gt;up to 1 minute 59 seconds &lt;/i&gt;could have passed!&lt;br /&gt;
&lt;br /&gt;
This is the nature of resolution: how often you can check the clock and it will tell you a different value. Now, computers do things a LOT faster than even once a second. Computers can do things in the &lt;i&gt;nanosecond &lt;/i&gt;range (1/1,000,000,000 of a second) or even faster! When I started up the Windows 10 machine that I&#39;m writing this post with, the resolution was 15.625 &lt;i&gt;milliseconds &lt;/i&gt;(~0.015 sec). That&#39;s WAY slower than 0.000000001 sec! In fact, that resolution will only check the clock 64 times per second, which can be slower than some frame rates that people get when playing King of the Kill.&lt;br /&gt;
&lt;br /&gt;
When we do windows programming, when we set a timer or tell a thread to sleep, we specify values in milliseconds (1/1,000 sec), but if it&#39;s only checking the clock every 15.625 milliseconds, a 1 millisecond timer can end up waiting 15.625 milliseconds, which is &lt;i&gt;more than a whole frame &lt;/i&gt;in some cases. Obviously we want Windows to check the clock much faster than 15.625 milliseconds.&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://media2.giphy.com/media/l2JhBVLe4msQnQYEg/giphy.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;288&quot; data-original-width=&quot;512&quot; height=&quot;180&quot; src=&quot;https://media2.giphy.com/media/l2JhBVLe4msQnQYEg/giphy.gif&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;This is about how often I check my phone. And I always forget to check the time.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;h4&gt;
Yes but does it WORK?&lt;/h4&gt;
&lt;div&gt;
At first I thought this tool might have some confirmation bias behind it, but after digging into it, i&#39;m going to say that it&#39;s &lt;b&gt;&lt;i&gt;plausible&lt;/i&gt;&amp;nbsp;&lt;/b&gt;that it has a positive effect on gameplay. Windows has but one internal timer, and it&#39;s shared by everything running on the system. When Daybreak titles start up, we tell Windows that we want 1 millisecond resolution on the system timer. But, to be good software citizens, we tell Windows to set the timer resolution back to what it was when the game is ending. Seems reasonable, right?&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;strike&gt;Now imagine that everything does that. Say you start up Some App(tm) that sets the timer resolution from 15 ms to 1 ms, then you start up King of the Kill that also tries to set it to 1 ms. However, then you shut down Some App. Thinking that it&#39;s a good software citizen also, it sets the timer back to 15 ms. But you&#39;re still playing King of the Kill! Now you might see some different behaviors, like getting micro-stutters, and miss hits that should have landed, etc. The game is doing what it&#39;s supposed to, but something happened that it didn&#39;t expect: the system timer got set back to low resolution. The Set Resolution Timer tool doesn&#39;t appear to continually update the Current Resolution display, but I believe it will try periodically to make sure the system timer is at the selected resolution.&lt;/strike&gt; EDIT: Lucas Hale (the author of SetTimerResolution) commented below to let me know that this assumption is invalid. It appears that Windows will take the maximum resolution requested by &lt;i&gt;any &lt;/i&gt;running application. So if the game client requests 1ms resolution, and SetTimerResolution requests 0.5ms resolution, it will take the latter. This is good as it makes the devs&#39; lives easier!&lt;/div&gt;
&lt;h4&gt;
How does Set Timer Resolution work?&lt;/h4&gt;
&lt;div&gt;
This section is not for the technically faint-at-heart. I&#39;m going to wax programmatically on you. First of all, when the game starts up, we call a documented function called &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx&quot;&gt;timeBeginPeriod&lt;/a&gt;&amp;nbsp;with the minimum value reported by the &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/dd757627(v=vs.85).aspx&quot;&gt;timeGetDevCaps&lt;/a&gt; function (generally 1 [millisecond]). This would probably work fine in many cases as long as our game is the only thing running on the machine. But that is never the case. Little programs that do behind-the-scenes things can start and end and do all sorts of stuff. Browsers can be running with multiple tabs open. Streamer software. Video recording software. Etc. If any of those things can affect the system timer while the game is running, then bad things happen.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
It looks like Set Timer Resolution goes even deeper than the multimedia functions (like timeBeginPeriod) that our titles are calling. It goes straight to the kernel, the heart of every operating system. It looks like it&#39;s calling some undocumented user-mode kernel functions:&amp;nbsp;&lt;a href=&quot;http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html&quot;&gt;NtQueryTimerResolution&lt;/a&gt; and &lt;a href=&quot;http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html&quot;&gt;NtSetTimerResolution&lt;/a&gt;. These are likely called deeper down from the multimedia functions that our titles use.&lt;/div&gt;
&lt;h4&gt;
So where do we go from here?&lt;/h4&gt;
&lt;div&gt;
I&#39;d like to make Set Timer Resolution completely unnecessary. Since the game is already trying to set the timer resolution at startup, it seems like we could be doing a better job of making sure it stays set. I&#39;ll evaluate this against our current priorities and talk with the team about getting this in an upcoming hotfix.&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://media3.giphy.com/media/3o7qDEq2bMbcbPRQ2c/giphy.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;350&quot; data-original-width=&quot;504&quot; height=&quot;222&quot; src=&quot;https://media3.giphy.com/media/3o7qDEq2bMbcbPRQ2c/giphy.gif&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Boom.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2017/07/resolute-timers.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4963533338816312534</guid><pubDate>Tue, 21 Feb 2017 07:59:00 +0000</pubDate><atom:updated>2017-07-04T18:05:20.386-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">daybreak</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">kotk</category><category domain="http://www.blogger.com/atom/ns#">planetside2</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Which patch? Dispatch.</title><description>**Authors note: I started writing this post a year ago. Hence the references are a bit dated, but the content is relevant.&lt;br /&gt;
&lt;br /&gt;
At the behest of the &lt;a href=&quot;https://www.reddit.com/r/Planetside/comments/435cwr/joshua_kriegshauser_on_twitter_so_i_may_have/&quot;&gt;good&lt;/a&gt; &lt;span id=&quot;goog_1865148173&quot;&gt;&lt;/span&gt;&lt;a href=&quot;https://www.reddit.com/r/Planetside/comments/437ggz/thank_you_for_the_cpu_fix/&quot;&gt;people&lt;/a&gt; &lt;span id=&quot;goog_1865148174&quot;&gt;&lt;/span&gt;of &lt;a href=&quot;https://www.reddit.com/r/Planetside/comments/4360ji/fps_fixed_hot_fix_for_performance_has_increased/&quot;&gt;reddit&lt;/a&gt;, I figured I would talk a little bit about a recent issue that cropped up in &lt;a href=&quot;https://www.planetside2.com/home&quot;&gt;Planetside 2&lt;/a&gt;: a runaway thread in our threading library that went mostly unnoticed and caused reduced performance.&lt;br /&gt;
&lt;h2&gt;
Threads Are Hard&lt;/h2&gt;
&lt;div&gt;
Writing good multi-threaded code &lt;a href=&quot;http://blog.smartbear.com/programming/why-johnny-cant-write-multithreaded-programs/&quot;&gt;isn&#39;t easy&lt;/a&gt;. I challenge the linked article&#39;s author in that multi-threaded programming is actually difficult. Sure, some of it boils down to programmers not following best practices, but there are several other facets that you don&#39;t encounter in single-threaded programming:&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Synchronization&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Reads and Writes to data must be synchronized&lt;/li&gt;
&lt;li&gt;Compiler&#39;s optimization effect may subtly effect program operation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://preshing.com/20120612/an-introduction-to-lock-free-programming/#different-processors-have&quot;&gt;Lock-less programming considerations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Possibility of dead-locking or live-locking&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Non-determinism. This basically means that the program doesn&#39;t run the same way every time. This has several repercussions:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Difficult-to-reproduce issues&lt;/li&gt;
&lt;li&gt;Testing/error logging can mask issues or create false positives (similar to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Observer_effect_(physics)&quot;&gt;Observer effect&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Statistical testing (problems occurring with a low enough frequency that you don&#39;t see them until they reach the Live environment)&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://i.imgur.com/9Hudnws.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://i.imgur.com/9Hudnws.png&quot; height=&quot;151&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Woody shares my expression.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
However, the availability of hardware is favoring power by having increasingly larger numbers of cores. To take advantage of that power, you need threads.&lt;br /&gt;
&lt;h2&gt;
Concurrency&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
Threads are a solution to the problem of trying to make a computer seem to do multiple things at the same time. In the olden days, threads didn&#39;t exist, but systems could start other processes by &lt;a href=&quot;http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/&quot;&gt;forking the process&lt;/a&gt;. This would create a copy of the process that could do different things without affecting the parent process. By being a copy, changes that one process made to its memory and variables wouldn&#39;t affect the other process. The two processes would still be able to communicate somewhat via &lt;a href=&quot;https://en.wikipedia.org/wiki/Inter-process_communication&quot;&gt;IPC&lt;/a&gt;, but this is generally much slower than, say, setting a variable within a process.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://artfabrik.com/wp-content/uploads/2012/11/DSCN0298.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://artfabrik.com/wp-content/uploads/2012/11/DSCN0298.jpg&quot; height=&quot;150&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Like my threads?&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
But sometimes you &lt;i&gt;want&lt;/i&gt;&amp;nbsp;one process to be able to do multiple things at the same time. That&#39;s where threads come in to play. Since threads allow multiple things to happen &quot;simultaneously&quot; within the same process, you have concurrency.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://i.imgur.com/xh4xG0q.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://i.imgur.com/xh4xG0q.png&quot; height=&quot;180&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
I put &quot;simultaneously&quot; in quotes, because multi-processor systems of yore were generally limited to server-class hardware. It was uncommon to find a user&#39;s home machine with more than one processor. This means that the system could really only do one thing at a time, but it &lt;i&gt;looked &lt;/i&gt;like it was doing things simultaneously because it was switching threads (i.e. things that it was doing) very, very quickly. These days, &lt;i&gt;everything &lt;/i&gt;is multi-processor. My house thermostat is probably multi-CPU (ok, not really). The focus in hardware shifted from doing one thing very fast (higher clock speed aka GHz on CPUs) to doing many things pretty fast at the same time. The previous generation consoles (Playstation 3 and Xbox 360) had three to four CPUs whereas today&#39;s console generation (PlayStation 4 and Xbox One) have eight. My work computer has 12 &quot;&lt;a href=&quot;https://en.wikipedia.org/wiki/Hyper-threading&quot;&gt;logical&lt;/a&gt;&quot; cores.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Early threading involved creating threads for very specific tasks. For example, EverQuest II largely runs in a single thread, but creates specific threads for loading files, talking to the streaming asset server, updating particle effects, etc. Most of the time those specific threads are doing nothing; they&#39;re just sleeping. As the number of processors in a system grew, it becomes less practical to have dedicated, specific threads, especially when the number of processors in a system differs from system to system.&lt;/div&gt;
&lt;h2&gt;
Synchronization&lt;/h2&gt;
&lt;div&gt;
Let&#39;s take a break for a second to talk about a related topic. &lt;i&gt;Synchronization&lt;/i&gt; is a big, huge, gargantuan topic wherein lies most of the problems with multi-threading. I&#39;m only going to say a few words about Synchronization.&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://www.funonthenet.in/images/stories/forwards/dance/pic1.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://www.funonthenet.in/images/stories/forwards/dance/pic1.jpg&quot; height=&quot;144&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Yep, like that.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
Nearly everything in the computer system can be considered a resource that must be shared: files, memory, CPU time, DVD drives, graphics, etc. What must be shared must be synchronized so that separate threads don&#39;t counter-productively stomp on each other. Process memory is probably the most often shared and problematic resource because &lt;i&gt;everything &lt;/i&gt;interacts with it. Something like a file is fairly easy to synchronize because nearly every access of it requires a function call. Memory, on the other hand... For instance, here is an example of a function that just increments a number. What would the value be if you had two threads on a multi-processor machine calling this function 1000 times?&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipfnL2gwaZsKJZt5CFXp9AgMDJ7zFiFco_C1FnWmZoirU0cr9gVJRcmGMtW851J2n327eBwc6_oM7H3DysP-6ENpEywLykz8mt8wnjjkyki1IBnOmnQEEjPUvOZsMKmYtJkVc04pHNvg/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipfnL2gwaZsKJZt5CFXp9AgMDJ7zFiFco_C1FnWmZoirU0cr9gVJRcmGMtW851J2n327eBwc6_oM7H3DysP-6ENpEywLykz8mt8wnjjkyki1IBnOmnQEEjPUvOZsMKmYtJkVc04pHNvg/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Hint: it&#39;s usually not 2000. Surprise! Incrementing a number is not an &lt;a href=&quot;https://en.wikipedia.org/wiki/Linearizability&quot;&gt;atomic operation&lt;/a&gt;, it&#39;s actually a read-modify-write operation.&amp;nbsp;This is one of those things that makes multi-threaded programming so hard! To protect the section of code that increments the number, you have to use some sort of synchronization primitive, like an &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/2ddez55b.aspx&quot;&gt;atomic intrinsic&lt;/a&gt;, &lt;a href=&quot;http://en.cppreference.com/w/cpp/thread/mutex&quot;&gt;mutex&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Spinlock&quot;&gt;spin-lock&lt;/a&gt;, or the like.&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;
Tasking&lt;/h2&gt;
&lt;div&gt;
When you have more processors available, it makes more sense to break problems down in to logical &lt;b&gt;tasks &lt;/b&gt;or units of work. Instead of having a dedicated thread to load files, now you just have a &quot;load file&quot; task. You have a &quot;collect garbage&quot; task. You have an &quot;animate entity&quot; task. Task, task, task.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://i.imgur.com/G9Nbo5Q.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://i.imgur.com/G9Nbo5Q.png&quot; height=&quot;150&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Khaaaaaaaannn!!!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
&lt;br /&gt;
This concept of tasks helps you to fill all available processors with work to do. Theoretically, if you can keep the task backlog full, CPUs will always have work to do. If you&#39;re using 100% of the available processors, you&#39;re doing the maximum amount of work that the system can do.&lt;/div&gt;
&lt;h2&gt;
Dispatch&lt;/h2&gt;
&lt;div&gt;
In 2009, Apple launched Mac OS X 10.6 with a programming API (libdispatch) marketed with the name&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Grand_Central_Dispatch&quot;&gt;Grand Central Dispatch&lt;/a&gt;&amp;nbsp;(GCD). It is, among other things, a generic task execution system. You have a function or a task that you want to run in the main thread or a separate thread at specific priority levels, immediately or at a scheduled time in the future. You just take that function or task and throw it on a &quot;dispatch queue&quot; and let it run. Simple. Powerful. Efficient. I quickly fell in love with what GCD could offer when I used it for my iPhone game, &lt;a href=&quot;https://itunes.apple.com/us/app/bust-a-mine/id860372543?mt=8&quot;&gt;Bust a Mine&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Fast forward to mid-2014. I became Technical Director of Planetside 2. The team was working on porting Planetside 2 to the PlayStation 4. Performance profiling was showing that the CPUs on the PS4 were slower than average CPUs on our PC players&#39; machines, and Planetside 2 was still largely single-threaded. We started looking at threading technologies like &lt;a href=&quot;https://www.threadingbuildingblocks.org/&quot;&gt;Intel&#39;s Threading Building Blocks&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;http://www.openmp.org/&quot;&gt;OpenMP&lt;/a&gt;, and even the &lt;a href=&quot;http://en.cppreference.com/w/cpp/thread&quot;&gt;C++11 thread support library&lt;/a&gt;. However, given my experience with libdispatch and the approach of looking at the problem as tasks rather than dedicated threads, we decided to look around for something similar. We found &lt;a href=&quot;http://opensource.mlba-team.de/xdispatch/docs/current/index.html&quot;&gt;xdispatch&lt;/a&gt;, a port of libdispatch to Windows and Linux (libdispatch was originally written for Mac OS X which is based on BSD). However, it had some issues: namely it didn&#39;t support the PS4 (few things did) and was based on a much older version of libdispatch. We began adapting it to the PS4 and it gave us a solid framework to start multi-threading Planetside 2.&lt;br /&gt;
&lt;h2&gt;
Adaptive Tuning&lt;/h2&gt;
&lt;div&gt;
We developed a threading sub-team on the PS4-on-PS2 project that had a primary requirement (increase performance) through two primary points of attack: 1) tune xdispatch to do what we needed and 2) adapt existing threads and operations into tasks that could be done concurrently. Ideally these changes would carry over to the PC version of the game as well.&lt;/div&gt;
&lt;br /&gt;
Both of these facets were challenging. On the tuning front we discovered that part of the reason that GCD works so well on Mac OS X is because the kernel--the core of the operating system itself--is controlling the dispatch scheduling. We didn&#39;t have that ability on the PS4, nor could we get the information about how busy the system is! We went several iterations on how to deal with this, but eventually settled on working around this by setting up some guidelines--the standard dispatch queues would only be used for CPU-intensive work (calculation, animation, decompression, etc.) and we would limit them to the number of CPUs available.&lt;br /&gt;
&lt;br /&gt;
The second facet was a much longer pole that would continue throughout the project. Converting existing dedicated threads and PS2&#39;s old job-queue system to Dispatch was easy and quickly done, but that didn&#39;t net nearly the performance that we would need to see to be viable on PS4. We would have to go much deeper. This would involve taking core aspects of PS2&#39;s main loop and breaking them into tasks--entity processing, physics, animation, rendering, etc. This is difficult to do with C++ because non-thread-safe side-effects are nearly impossible to find; we would have to identify everything non-thread-safe that was happening in the single-threaded code before converting it to tasks.&lt;br /&gt;
&lt;h2&gt;
OMG Bugs&lt;/h2&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://i.imgur.com/n4lbjpJ.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://i.imgur.com/n4lbjpJ.png&quot; height=&quot;320&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;That was the song, right?&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The &lt;a href=&quot;https://www.reddit.com/r/Planetside/comments/435cwr/joshua_kriegshauser_on_twitter_so_i_may_have/&quot;&gt;reddit post&lt;/a&gt; that originally spawned this blog post sheds some insight on to a problem that still existed &lt;i&gt;a year &lt;/i&gt;after the PS4 version originally launched. Namely, PC players identified that a thread would take a whole CPU, but they found that if they killed it, performance got better and nothing bad really happened (or was not immediately visible). This was found to be a bug in timers in xdispatch that, to my knowledge, still exists. You can read the above link for more technical information, but it had to do with bad assumptions in the PC port of software originally written for BSD. Shockingly, the problem also existed in the PS4 build even though it shouldn&#39;t have. It looks like the timer implementation in xdispatch (and the libdispatch version that it was based on) was functional but not very efficient, so we wrote a new timer outside of xdispatch and used that instead.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Still later, we finally got a handle on one of our long-standing (post-PS4-launch) crash bugs. This was a crash bug that we had never seen internally (see my above point about bugs becoming a statistical problem). It &lt;i&gt;looked &lt;/i&gt;like a &lt;a href=&quot;https://en.wikipedia.org/wiki/Memory_corruption&quot;&gt;memory corruption&lt;/a&gt; problem, which just made all of the programmers reading this shudder in horror. Memory corruption is terrible. It is an evil problem and few good tools exist to locate it &lt;i&gt;assuming &lt;/i&gt;you can figure out how to make it happen. But find it we did, and it was also an issue with converting systems to multi-threading. In this case, an &#39;animation complete&#39; flag was in a data structure that was getting freed before the task performing the operation finished and set the flag. Since the memory was freed before the flag was set, sometimes the memory had been reused for other things, hence the corruption. This was a problem not with Dispatch itself, but with how a previously single-threaded operation had been converted to a task.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Most recently, we began hearing reports of &#39;lock-ups&#39; and &#39;hangs&#39; from PS4 players. This coincided with an update to PS4 SDK 3.500 (from 2.000) for Planetside 2, which, among other things, gave us an additional &lt;i&gt;half&lt;/i&gt;&amp;nbsp;of a CPU to use for game logic (with 2.000 the system would reserve two whole CPUs for itself, whereas after 3.000 it &lt;i&gt;only&lt;/i&gt;&amp;nbsp;used one-and-a-half). Because of this, we ramped up Dispatch (now a completely retooled version no longer based at all on xdispatch but based on a more current version of libdispatch) to take advantage of that half-a-CPU. Eventually we determined this to be the cause of the lockup, but for unexpected reasons. The game was not experiencing a dead-lock, but a form of&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Deadlock#Livelock&quot;&gt;&lt;i&gt;live&lt;/i&gt;-lock&lt;/a&gt;; all of the CPUs were running threads, they just weren&#39;t making any progress. This was because of a degenerate case between the design of libdispatch and the PS4 scheduler. Basically, the internals of libdispatch (and our Dispatch library that was based on it) are lock-less--they are doing atomic operations rather than locking mutexes. Some of these atomic operations are loops waiting for conditions to be met, or multiple&amp;nbsp;&lt;a href=&quot;http://x86.renejeschke.de/html/file_module_x86_id_41.html&quot;&gt;compare-and-exchange&lt;/a&gt; operations in a loop; they try to do an operation based on old information and retry with updated information if it fails. But the PS4 scheduler &lt;i&gt;will not run &lt;/i&gt;any thread of a lower priority if a higher priority thread is runnable. We could end up in a state where a lower-priority thread would be preempted after it changed conditions for a higher-priority thread. This would cause a sort of &lt;a href=&quot;https://en.wikipedia.org/wiki/Priority_inversion&quot;&gt;priority inversion&lt;/a&gt;&amp;nbsp;that would &lt;i&gt;never &lt;/i&gt;resolve. Most operating systems will at least give &lt;i&gt;some &lt;/i&gt;time to lower priority threads to prevent &lt;a href=&quot;https://en.wikipedia.org/wiki/Starvation_(computer_science)&quot;&gt;starvation&lt;/a&gt;, but the PS4 does not. Indeed the default scheduler for the PS4 is the FIFO scheduler, but even the round-robin scheduler will not run lower-priority threads. Our solution to this involved applying a progressive algorithm that would eventually put threads to sleep in extreme cases in order to allow the live-lock to resolve. Generally this &lt;i&gt;might &lt;/i&gt;look like a slight momentary dip in frame rate or may not even be noticed at all.&lt;/div&gt;
&lt;h2&gt;
Looking Forward&lt;/h2&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://dsd.me/wp-content/uploads/2014/08/Screen-Shot-2014-08-14-at-19.22.09.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://dsd.me/wp-content/uploads/2014/08/Screen-Shot-2014-08-14-at-19.22.09.png&quot; height=&quot;160&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Make sure your blinker is on.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We&#39;re always looking for ways to increase performance across all platforms. As other Daybreak games ramp up we&#39;re finding new ways to eke out increasing frame-rates and sharing that knowledge among the teams. Our internal Dispatch implementation is moving into other Daybreak titles and future projects, and it all started here, on Planetside 2. Efforts have been made to keep these types of changes in parity between the different games.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2017/02/which-patch-dispatch.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipfnL2gwaZsKJZt5CFXp9AgMDJ7zFiFco_C1FnWmZoirU0cr9gVJRcmGMtW851J2n327eBwc6_oM7H3DysP-6ENpEywLykz8mt8wnjjkyki1IBnOmnQEEjPUvOZsMKmYtJkVc04pHNvg/s72-c/Capture.PNG" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-310617041338986616</guid><pubDate>Thu, 26 Nov 2015 01:13:00 +0000</pubDate><atom:updated>2015-11-25T17:14:35.013-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">daybreak</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">planetside2</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Don&#39;t Repeat Yourself</title><description>Something that I&#39;ve been working with the &lt;a href=&quot;http://planetside2.com/&quot;&gt;Planetside 2&lt;/a&gt;&amp;nbsp;team lately has been a more strategic direction in how we write maintainable code for the future. Part of my direction for this has been adherence to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;D.R.Y. principle&lt;/a&gt;--Don&#39;t Repeat Yourself. Namely, instead of distributing authority out by the expression of Interfaces (and writing a lot of repetitive code), we are architecting unambiguously authoritative systems that can handle all functionality in a common way.&lt;br /&gt;
&lt;br /&gt;
One of our most recent realizations of this principle is in our new UI data-source layer.&lt;br /&gt;
&lt;br /&gt;
Previously, Planetside 2 (and previous&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Forgelight_Engine&quot;&gt;Forgelight Engine&lt;/a&gt; ancestors) had a concept of a DataSource interface. This was a simple interface that allowed sub-systems (such as Skills or Items) to express data in a string-based row/column format to the UI layer (Planetside 2 uses Scaleform GFx, a Flash-based interpreter/renderer). The Actionscript 3 UI code could find a DataSource by name and query it in the ways expressed by the interface (it could also register as a listener to determine if anything changed).&lt;br /&gt;
&lt;pre&gt;class IUIDataSourceTable {
public:
    virtual int GetData(int row, int column, String&amp;amp; output) = 0;
    virtual int GetRowCount() = 0;
    virtual int GetColumnCount() = 0;
    virtual String GetColumnName(int column) = 0;
    virtual void AddListener(IUIListener*) = 0;
    virtual void RemoveListener(IUIListener*) = 0;
    virtual void NotifyChanges() = 0;
};&lt;/pre&gt;
&lt;br /&gt;
Seems great so far, right?&lt;br /&gt;
&lt;br /&gt;
Because this is an interface, each system is responsible for surfacing internal data to the UI through the GetData call. But each system can implement it completely different. As long as &#39;output&#39; receives the value of a given cell, where that data comes from isn&#39;t important to the UI layer. Also, detecting when that data changed (and notifying listeners) became the responsibility of each system.&lt;br /&gt;
&lt;br /&gt;
Okay... So?&lt;br /&gt;
&lt;br /&gt;
Well, now let&#39;s say that you want to do more complex operations with that data, such as sorting, filtering or joining with another IUIDataSourceTable. Sure we could add additional functions to the IUIDataSourceTable, but as our codebase grows, we&#39;re faced with a quandary: how do shrinking development teams maintain ever-growing codebases??&lt;br /&gt;
&lt;br /&gt;
Let&#39;s look at it a different way. Say I wanted to add a Filter function to my IUIDataSourceTable. Planetside 2 has on the order of about 130 implementations of this interface (give or take) in very disparate systems such as Character Select, Skills, Items, Marketplace, Social, Experience, etc. If I want to add another function to my interface, I&#39;m looking at actually writing 130 functions and doing a lot of research into those systems to find out how they actually surface the data.&lt;br /&gt;
&lt;br /&gt;
This is the problem with distributed authority: it exponentially extends a simple change. Work-arounds in the past involving just two things that we always want to do--filtering and sorting--required either a C++ engineer to add to a specific DataSource implementation, or a UI engineer to gather everything and sort it in Actionscript 3. This is not a very productive or sustainable model and it&#39;s ridiculously time intensive (both on the CPU and in development time).&lt;br /&gt;
&lt;br /&gt;
Ok, now let&#39;s adopt a don&#39;t-repeat-yourself strategy.&lt;br /&gt;
&lt;br /&gt;
Instead of declaring an Interface that each system must adhere to, let&#39;s design a system that is unambiguously authoritative in terms of UI data. We want one system that we can tune and add functionality to. And since we&#39;re talking about UI datasources, can you think of anything that is designed to store row/column data and has sorting and filtering capabilities? Maybe... like... a database?! Turns out there is such a beast that we can use as the core of our new authoritative UI data system: &lt;a href=&quot;https://www.sqlite.org/&quot;&gt;sqlite3&lt;/a&gt;! This gives us a light-weight, in-memory SQL database that we can use to store our data and slice it any way we need! And better yet, browsers (and even mobile devices) have been using it for years!&lt;br /&gt;
&lt;br /&gt;
After getting an engineer excited about this prospect and putting her on the project, she turned out an awesome system called uiDB that exposed game data to the Flash-based UI through SQL and tables. This has some really cool features:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Adding new functionality is trivial. We just added some very broad functionality in less than a day because we changed one authoritative system instead of 130 interface implementations.&lt;/li&gt;
&lt;li&gt;sqlite has a callback mechanism, so we can use Actionscript 3&#39;s event mechanism to notify the UI when data changes--automatically. C++ just updates the game data in the uiDB system as it changes, and the authoritative uiDB system notifies any listeners. No extra code required.&lt;/li&gt;
&lt;li&gt;The AS3 code can create a view to slice, sort, filter, or join data however it wants and can still get notified when any of that data changes. Without having to write any additional C++ code.&lt;/li&gt;
&lt;li&gt;The AS3 code can also perform any SQL query on the in-memory UI database whenever it wants. Need data one time? No problem. Need to display data and refresh when it changes? Equally trivial.&lt;/li&gt;
&lt;li&gt;We wrote a &lt;a href=&quot;http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/fl/data/DataProvider.html&quot;&gt;DataProvider&lt;/a&gt;&amp;nbsp;implementation for uiDB tables and views. This is the standard DataProvider that Flash widgets use, so our UI engineers don&#39;t have to write any code to handle change events or anything--the DataProvider can do all of that and the UI just updates automatically. Talk about code reduction!&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Another surprise out of changing from the interface to authoritative model is that our UI data performance increased, even though we were now using a SQL-interpreting database! This is because of a few different reasons:&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;In the Interface model, each system retrieved its data through different means. Some could be woefully inefficient.&lt;/li&gt;
&lt;li&gt;sqlite3 is a well-tested, highly optimized, tightly-packed, small C library. It has a development team that cares only about its performance and functionality in a variety of settings. As such, we are gaining the wealth of knowledge and experience of another team focusing intently on one piece of technology, allowing us to focus on making a game. (This could branch into another discussion about utilizing as much third-party technology as makes sense).&lt;/li&gt;
&lt;li&gt;By consolidating authority in one system instead of spreading it out, we had one target to focus on for performance testing and optimization.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
We are continuing to provide additional functionality through this system to our UI engineers in order to make their lives easier (and we&#39;re doing this across the board for all disciplines). This is looking forward to the future of Daybreak Games and the Forgelight Engine as much as the future of Planetside2.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
All in all, work smarter, save time across the board, and don&#39;t repeat yourself!&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2015/11/dont-repeat-yourself.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-6790385279137750180</guid><pubDate>Mon, 30 Jun 2014 07:36:00 +0000</pubDate><atom:updated>2014-06-30T00:36:59.358-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">github</category><category domain="http://www.blogger.com/atom/ns#">libraries</category><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">thor</category><title>By the Library of Thor!</title><description>I recently uploaded to github a &lt;a href=&quot;https://github.com/jkriegshauser/thor/&quot;&gt;C++ template library called Thor&lt;/a&gt;, which is something that I&#39;ve been playing around with. It&#39;s really just a way for me to write some crazy template code--a creative outlet if you will. This blog post is meant to show off some of the things that I&#39;m proud of in Thor. The name Thor came in part because of inspiration from &lt;a href=&quot;http://sourceforge.net/u/aandrei/profile/&quot;&gt;Andrei Alexandrescu&lt;/a&gt;&#39;s template library &lt;a href=&quot;http://loki-lib.sourceforge.net/&quot;&gt;Loki&lt;/a&gt;&amp;nbsp;and his book &lt;a href=&quot;http://erdani.com/index.php/books/modern-c-design/&quot;&gt;Modern C++ Design&lt;/a&gt;&amp;nbsp;(an excellent advanced C++ book).&lt;br /&gt;
&lt;br /&gt;
Some things in my Thor library are just me implementing things in the way I would if I had full control, and some are a slightly different take on popular concepts. Some things are already outmoded based on offerings in C++11 (especially in terms of atomic support), but are included for posterity.&lt;br /&gt;
&lt;br /&gt;
I apologize that the code samples are images (Blogger currently doesn&#39;t do well with template syntax), but generally a link is provided to the code on github.&lt;br /&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Containers&lt;/h3&gt;
The C++ standard library includes several standard, well-known and well-documented containers, however these containers don&#39;t often allow for much customization. For instance, std::vector (up until the C++11 standard) had no obvious means to free memory (C++11 adds the shrink_to_fit() function). Although &lt;a href=&quot;http://kera.name/articles/2010/08/it-is-not-called-the-stl-mmkay/&quot;&gt;not technically correct&lt;/a&gt;, I&#39;ll refer to the C++ standard library as STL below for brevity&#39;s sake.&lt;br /&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
thor::basic_string&lt;/h4&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/basic_string.h&quot;&gt;Thor&#39;s string class&lt;/a&gt; works very similarly to &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;std::string&lt;/span&gt; and &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;std::wstring&lt;/span&gt; with a few changes. First, the strings are atomically reference counted, so the underlying string data is shared when possible. Secondly, a template parameter can be specified to pre-reserve (as part of the basic_string instance, not a separate heap allocation) space for the string. The default of zero is a fully dynamic string, but if you need a quick string on the stack and don&#39;t want to allocate anything from the heap, it is easily done with the addition of a size template paramter:&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBmheY4fAffVbA3cV_OT3D8O2hnjM6kitoniww-3TXtdJherDupyFEKHRsaOeN8_NNQuX7eP1tqR-qE70cX0C48pSPAfLtfnop1GtpqjrRlhYQfgslUZxdvqeYjp1v9dZ8IOz2_3LGwQ/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBmheY4fAffVbA3cV_OT3D8O2hnjM6kitoniww-3TXtdJherDupyFEKHRsaOeN8_NNQuX7eP1tqR-qE70cX0C48pSPAfLtfnop1GtpqjrRlhYQfgslUZxdvqeYjp1v9dZ8IOz2_3LGwQ/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Furthermore, in addition the STL constructors for basic_string, additional options are available for printf-style formatting and literal strings (that don&#39;t require copying or allocation):&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMJsefXSr1QXwJiy_3Ld-_C9vJNLJMg4S1HEnba1w_kKtSxlEcFreHvTf1xbK2kHwLrf8o-PlKJ_fQjQk35fyjZFgJmPrM41w6juxdW7znZ_j8Syly1vBWGl0MZaCD4M9nARiPF6uoIw/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMJsefXSr1QXwJiy_3Ld-_C9vJNLJMg4S1HEnba1w_kKtSxlEcFreHvTf1xbK2kHwLrf8o-PlKJ_fQjQk35fyjZFgJmPrM41w6juxdW7znZ_j8Syly1vBWGl0MZaCD4M9nARiPF6uoIw/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The printf-style formatting is also supported for append, insert, replace and assign variants.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Thor&#39;s string also supports conversion between &lt;a href=&quot;http://en.wikipedia.org/wiki/UTF-8&quot;&gt;UTF-8&lt;/a&gt; and &quot;wide&quot; (&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;wchar_t&lt;/span&gt;). If &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;wchar_t&lt;/span&gt; is 32-bits, then the conversion will be between UTF-8 and &lt;a href=&quot;http://en.wikipedia.org/wiki/UTF-32&quot;&gt;UTF-32&lt;/a&gt;, however the more likely case is that &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;wchar_t&lt;/span&gt; is 16-bits, in which case the conversion will be between UTF-8 and UTF-16, complete with support for &lt;a href=&quot;http://en.wikipedia.org/wiki/UTF-16&quot;&gt;UTF-16 surrogate pairs&lt;/a&gt;.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg36mGOcYcMHrZv-w0A-BBH75UvK4_PgyY-u_xbdWF8l2IX96VVqgNOCI66kwDnwe0ORZF-pN5B0dBo2ZJHRwH5CfRY1UXKJziTCSgmj-H1tcPUD3B_LoXE9nqRr4VL8yCvrsDmcUz0AA/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg36mGOcYcMHrZv-w0A-BBH75UvK4_PgyY-u_xbdWF8l2IX96VVqgNOCI66kwDnwe0ORZF-pN5B0dBo2ZJHRwH5CfRY1UXKJziTCSgmj-H1tcPUD3B_LoXE9nqRr4VL8yCvrsDmcUz0AA/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
thor::list&lt;/h4&gt;
&lt;div&gt;
&lt;/div&gt;
In general node-based STL containers (such as std::list) will allocate a &#39;dead node&#39; or a terminator node from the heap as soon as they&#39;re constructed (even default constructed). Usually this terminator node will include space for the &#39;T&#39; stored type, although that space will never be used and therefore wastes space. I frown upon the fact that wasted space is allocated from the slower heap for such a simple operation as default constructing a list.&lt;br /&gt;
&lt;br /&gt;
Consider &lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/list.h&quot;&gt;Thor&#39;s list class&lt;/a&gt;. Instead of heap-allocating a terminator node, the head/tail pointers for the list class use a list_node_base and the &lt;i&gt;address of &lt;/i&gt;the list_node_base is used as the terminator. Thus, the terminator (used by the end() iterator) is implemented as such:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJAXAwIqwGFDJo_jNi-sqDT5Ef_zXjmCHeCLI3g5hXavJFOvTkJUJCnd0-l2zBC5ApUN-mVVLHElOf3WwcM72wbdd4WFly_qufH70UZIwcaUivVwVu4VTIH3pm-cDIaJQGwdgwXHs11g/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJAXAwIqwGFDJo_jNi-sqDT5Ef_zXjmCHeCLI3g5hXavJFOvTkJUJCnd0-l2zBC5ApUN-mVVLHElOf3WwcM72wbdd4WFly_qufH70UZIwcaUivVwVu4VTIH3pm-cDIaJQGwdgwXHs11g/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
This has the advantage of not requiring a heap allocation for the terminator node while still allowing for simple reverse iteration and no need for null-pointer checking at head/tail. However, there are two disadvantages. The first is that any accidental writes to the end() iterator (which is completely invalid anyways) may cause memory corruption. This can be prevented in checked/debug builds with asserts when trying to dereference the end() iterator. The second is that swap semantics require slightly more finesse (although these are more complicated in Thor for another reason explained below). In std::list the swap() function must only exchange pointers and counts between two lists--since the terminator node is allocated on the heap it is not tied to either list and is automatically part of the swap. However, since Thor&#39;s list essentially has a local terminator node as part of the instance, care must be taken to fix up the terminators during swap:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrTxgCyYgmsXaSrAWgHXV2eoL2gcYjTNyfgNf3vdOnAoa_4tkXBWq41tHuuASK3pIxsxzJvbSLcofBR7VzQBX9R9NronRa9ok7lPk42Xkns5KrU_prsJSxPeG_1aLkvO9HlVKwM-kzeA/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrTxgCyYgmsXaSrAWgHXV2eoL2gcYjTNyfgNf3vdOnAoa_4tkXBWq41tHuuASK3pIxsxzJvbSLcofBR7VzQBX9R9NronRa9ok7lPk42Xkns5KrU_prsJSxPeG_1aLkvO9HlVKwM-kzeA/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Notice that the swap function handles a concept of shareable. This is because of another enhancement of Thor&#39;s containers. Essentially, space for a certain number of items can be pre-allocated along with the container, similarly to the string class mentioned above. For instance, &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;thor::list&amp;lt;T,5&amp;gt;&lt;/span&gt;&lt;t 5=&quot;&quot;&gt;&amp;nbsp;specifies that no heap allocations must occur for the first five list entries. Fortunately, &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;thor::list&amp;lt;T,5&amp;gt;&lt;/span&gt;&lt;t 5=&quot;&quot;&gt;&amp;nbsp;inherits from &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;thor::list&amp;lt;T&amp;gt;&lt;/span&gt;&lt;t&gt;&amp;nbsp;so that it can be used anywhere where a list&lt;t&gt; is required. However, it also means that the problem of &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;swap()&lt;/span&gt; now must handle pre-allocated nodes. It is less efficient to &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;swap()&lt;/span&gt; a list with pre-allocated nodes as this requires the nodes be converted to heap allocated nodes.&lt;/t&gt;&lt;/t&gt;&lt;/t&gt;&lt;/t&gt;&lt;br /&gt;
&lt;br /&gt;
There are many other extensions to all of the containers, yet they all try to remain true to the C++ standard library specification.&lt;br /&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
thor::hash_map (et al)&lt;/h4&gt;
The &lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/hashtable.h&quot;&gt;thor::hashtable class&lt;/a&gt;&amp;nbsp;(which forms the basis of hash_map, hash_set and the multi- versions of each) has a noteworthy feature: a &lt;a href=&quot;http://en.wikipedia.org/wiki/Policy-based_design&quot;&gt;policy class&lt;/a&gt;&amp;nbsp;that controls how the stored values are organized into buckets. The options are a power-of-two system that is very fast by using bitwise masks instead of divide/mod instructions (although this method can be terrible for pointers as keys as they typically are multiples of 4 or 8 and the bucket strategy loses efficiency leading to collisions), or a more traditional (and slower yet less collision-prone) prime-number strategy.&lt;br /&gt;
&lt;br /&gt;
The C++ standard library hash containers are undefined order containers. This means that the order in which you insert items is not necessarily the same order in which you would iterate over the items. However, thor&#39;s hash containers are a fusion of a list and a hashtable, which allows for a defined iteration order as well as amortized O(1) lookup time. This does require a slight change to how iteration starts: the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;begin()&lt;/span&gt; function defaults to &#39;list&#39; iteration mode but can be changed to &#39;hash&#39; iteration mode, whereas &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;find()&lt;/span&gt; operations default to &#39;hash&#39; iteration mode.&lt;br /&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
Embedded Containers&lt;/h4&gt;
&lt;div&gt;
For &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;std::list&lt;/span&gt; (or &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;thor::list&lt;/span&gt; for that matter), the &#39;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;T&lt;/span&gt;&#39; type that you&#39;re storing in the container is contained within a node that tracks other information for the container. If you had a list of pointers to objects in the heap, then the objects were allocated and the list container must allocate a small node to store the pointer. This is wasteful. The embedded containers can alleviate this by having the node tracking information as part of the object that is being stored. For instance, the &#39;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;T&lt;/span&gt;&#39; type stored in a &lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/embedded_list.h&quot;&gt;thor::embedded_list&lt;/a&gt;&amp;nbsp;must contain an &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;embedded_list_link&lt;/span&gt; member and the member is given as a template parameter to &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;thor::embedded_list&lt;/span&gt;.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgynGjkPpUdm8u1P2tGcxfpDa2YY9rFsCiRAAqFS5f8pew_OtSGk-YtyccJuJsEsqlTGk1gPTtr3sp87LbbEgaLpuQOzj9gI0mc6THmejIpwYSKR4WkDnRtBDRppPF58XsmmKDPYa0UJg/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgynGjkPpUdm8u1P2tGcxfpDa2YY9rFsCiRAAqFS5f8pew_OtSGk-YtyccJuJsEsqlTGk1gPTtr3sp87LbbEgaLpuQOzj9gI0mc6THmejIpwYSKR4WkDnRtBDRppPF58XsmmKDPYa0UJg/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Atomic integer/pointer&lt;/h3&gt;
&lt;div&gt;
The &lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/atomic_integer.h&quot;&gt;thor::atomic_integer&lt;/a&gt; system is similar to C++11&#39;s &lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;atomic&lt;/span&gt;&amp;nbsp;wrapper, so while not completely necessary it was a fun exercise to write. The real meat of how it works is based in the platform-specific interlocked system (the Windows version is &lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/win/interlocked_win.h&quot;&gt;interlocked_win&lt;/a&gt;). This uses template specialization to handle one-, two-, four- and eight-byte integers. By using intrinsics, these atomic_integers actually compile down to very few inline instructions for most operations. Consider how declaring interlocked&lt;int&gt; works on a platform with a 32-bit integer. First, some magic happens based on how the non-specialized interlocked class is declared. There is a second template parameter that is required, but defaults to sizeof(T):&lt;/int&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7DNi8JbngISXLgWtRxVnBC17wO_StxeMuvjLy_24DdB2X6XtAeIkQ-woTKoCkz-QFDTWBDD2Cft-AFNMsTXCrki07yLMK6KzjCH3x9hpq2Na3ZCnvs6n_ZJ-sE1VqrUCsjWeMehiJkA/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7DNi8JbngISXLgWtRxVnBC17wO_StxeMuvjLy_24DdB2X6XtAeIkQ-woTKoCkz-QFDTWBDD2Cft-AFNMsTXCrki07yLMK6KzjCH3x9hpq2Na3ZCnvs6n_ZJ-sE1VqrUCsjWeMehiJkA/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The template parameter &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;T_SIZE&lt;/span&gt; defaults to 4 for &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;int&lt;/span&gt;, which causes a specialization to be selected:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK1kXjvUqz9ufwblmCfPmY_XfILLRcrCn4w0wKhoCBTJ3Y8fFqPNKPDfwD5j1uOkFbNhEzaZ6YcX2DKAStuJ7-fyYAQZsMxKMeVNzSjCLwo2YTnolg5iozg7MYQiX-5MQbCMycOi7VkA/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK1kXjvUqz9ufwblmCfPmY_XfILLRcrCn4w0wKhoCBTJ3Y8fFqPNKPDfwD5j1uOkFbNhEzaZ6YcX2DKAStuJ7-fyYAQZsMxKMeVNzSjCLwo2YTnolg5iozg7MYQiX-5MQbCMycOi7VkA/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
This specialization uses the correct functions/intrinsics for the numeric type that we&#39;re wrapping in atomic_integer. This allows &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;atomic_integer&lt;/span&gt; to do the correct operation based on size of the integer parameter:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxVqCwbxFtf6xhfRH4_MoLophW_ZmHzTlLT9zN1E2TUpBzNh-sb6eZ9aDODgq1Whl4oT_X-vL8SrGjkPqKXwzThqoZfqI2cNyeb1xKxQqp1zrFUoymIeGWdjCAhLzwNem6pc-R1VUkpA/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxVqCwbxFtf6xhfRH4_MoLophW_ZmHzTlLT9zN1E2TUpBzNh-sb6eZ9aDODgq1Whl4oT_X-vL8SrGjkPqKXwzThqoZfqI2cNyeb1xKxQqp1zrFUoymIeGWdjCAhLzwNem6pc-R1VUkpA/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Memory Alignment&lt;/h3&gt;
&lt;div&gt;
Another place where Thor uses template specialization is with memory alignment. All of the containers do memory allocation through Thor&#39;s &lt;a href=&quot;https://github.com/jkriegshauser/thor/blob/master/memory.h&quot;&gt;memory functions&lt;/a&gt;. There is a simple class called the align_selector:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnmRlyj7YKZgtpZ-PuEKkomV7gjwmqXq7Gfe4Exq5-bSqOoaDNqblcc6xBIvdPA-Rbff2SWhZoRYCtkF54jBfcmKXgg2MGNWqD2XM9FKwD-jz2QRteSD52cxzEIQk1iC4lI62XFOiLpg/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnmRlyj7YKZgtpZ-PuEKkomV7gjwmqXq7Gfe4Exq5-bSqOoaDNqblcc6xBIvdPA-Rbff2SWhZoRYCtkF54jBfcmKXgg2MGNWqD2XM9FKwD-jz2QRteSD52cxzEIQk1iC4lI62XFOiLpg/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
This class&#39;s only job is to determine if an object can use system-guaranteed alignment or not (most objects can). If the system alignment will suffice, alignment_selector::alignment will be zero. The align_alloc class uses the align_selector as a default template parameter to select the proper specialization for the alignment:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDjGxBEvwMQC7AaJS-xBXkkLS_O8uAFUnpz1FhwOrK_dzZzxkYuQmL5p-gVAGlLo4Iq_RMTdfdboGppXYFFppNN390MzXH4vRARTotpjofTxJzNIFNAGUuW6NL_PDWNvq0ilf5GzKLBA/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDjGxBEvwMQC7AaJS-xBXkkLS_O8uAFUnpz1FhwOrK_dzZzxkYuQmL5p-gVAGlLo4Iq_RMTdfdboGppXYFFppNN390MzXH4vRARTotpjofTxJzNIFNAGUuW6NL_PDWNvq0ilf5GzKLBA/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The system default alignment just allocates raw bytes:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM4EJ9UNxi0REbTv63maMlBrivCoXzmL0Mof0bqix7snMLbda7J8U0I4YY_DqR5Bk6LXepv7PjGjcxJxLReDDFhLjoE0brNI1vfUe0cESW4M8hQcCtbAZPgXke0bQodqZkNyCfIHB65Q/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM4EJ9UNxi0REbTv63maMlBrivCoXzmL0Mof0bqix7snMLbda7J8U0I4YY_DqR5Bk6LXepv7PjGjcxJxLReDDFhLjoE0brNI1vfUe0cESW4M8hQcCtbAZPgXke0bQodqZkNyCfIHB65Q/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
But the default version that handles non-default alignments will over-allocate and offset:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8bFWQuPuZt2PqKLZSUWtPFflWt9-UkPool90x63WL9ayrgg0mKX2Qlt1vsMvVyzpv6mmoKxbFkw5QgiJN_GGGZStnUZhyphenhyphenTlWpkOeLlzbP9BggU0ehQkt6MUlmlzzScNrN3o7iGM67eQ/s1600/Capture.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8bFWQuPuZt2PqKLZSUWtPFflWt9-UkPool90x63WL9ayrgg0mKX2Qlt1vsMvVyzpv6mmoKxbFkw5QgiJN_GGGZStnUZhyphenhyphenTlWpkOeLlzbP9BggU0ehQkt6MUlmlzzScNrN3o7iGM67eQ/s1600/Capture.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Arguably, this version could use &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;_aligned_malloc&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;or &lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;memalign&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;or similar, but by using &lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;new[]&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt; an application may still override &lt;/span&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt; and use their own memory manager if so desired.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;There is a lot more to the Thor library. &lt;a href=&quot;https://github.com/jkriegshauser/thor/&quot;&gt;Take a look at it on github&lt;/a&gt; and let me know what you think.&lt;/span&gt;&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2014/06/by-library-of-thor.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBmheY4fAffVbA3cV_OT3D8O2hnjM6kitoniww-3TXtdJherDupyFEKHRsaOeN8_NNQuX7eP1tqR-qE70cX0C48pSPAfLtfnop1GtpqjrRlhYQfgslUZxdvqeYjp1v9dZ8IOz2_3LGwQ/s72-c/Capture.PNG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3418457988704189602</guid><pubDate>Thu, 15 May 2014 23:12:00 +0000</pubDate><atom:updated>2014-05-15T23:21:29.852-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">games</category><category domain="http://www.blogger.com/atom/ns#">ipad</category><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">ipod</category><category domain="http://www.blogger.com/atom/ns#">mobile development</category><title>Busting Mines</title><description>&lt;div style=&quot;text-align: center;&quot;&gt;
Behold: &lt;a href=&quot;https://itunes.apple.com/app/bust-a-mine/id860372543&quot;&gt;Bust A Mine&lt;/a&gt;. My first iOS development project.&lt;/div&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg02acLSubcOvYfOmVd0mKBDXkqzjiSSjL3mLX176r4DvnFONb2YaoZ3HX84NJki_A6aTbcLaycBmC1zTvSKFCacyuyM3fNeEyUc1uK-EgsdBRSlDvUTUT98WU2_jCxTo1lBOi71IwXJw/s1600/bustamine.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg02acLSubcOvYfOmVd0mKBDXkqzjiSSjL3mLX176r4DvnFONb2YaoZ3HX84NJki_A6aTbcLaycBmC1zTvSKFCacyuyM3fNeEyUc1uK-EgsdBRSlDvUTUT98WU2_jCxTo1lBOi71IwXJw/s1600/bustamine.jpeg&quot; height=&quot;180&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Not a bad run!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Inspiration&lt;/h2&gt;
When I set out to (once again) try my hand at iOS development, I didn&#39;t know what the future would hold. It was around the time that a former game that I worked on,&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Clone_Wars_Adventures&quot;&gt;Clone Wars Adventures&lt;/a&gt;,&amp;nbsp;had announced that it would be ending on 31 March 2014. One of my favorite mini-games in CWA was called &lt;a href=&quot;https://www.youtube.com/watch?v=MU3MR4Q4UKI&quot;&gt;Mine Buster&lt;/a&gt;, a simple game that involved creating a chain reaction for points. It was also a mini-game that would soon cease to exist. And a good mini-game that could use a Spiritual Successor.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Beginnings&lt;/h2&gt;
I started working on my own rendition in early March while professionally employed as the Director of Technology at &lt;a href=&quot;http://moltengames.com/&quot;&gt;Molten Games&lt;/a&gt;. Within two weeks of late-night spare time I had a working prototype that functioned similarly to its inspiration. I had heard good things about using &lt;a href=&quot;http://cocos2d-iphone.org/&quot;&gt;Cocos2D&lt;/a&gt;&amp;nbsp;3.0 and it seemed to fit the bill. My prototype was still very rough and could only run on the iOS Simulator that comes with Xcode 5. However, I was happy enough with the progress that I decided to spring for the iOS Developer Program so that I could actually run and debug on my iPhone and iPad. There was a little learning curve to overcome as running code on the device requires creating App Identifiers, Certificates for code signing and Provisioning Profiles for signed code to be allowed to run on the device. Being able to run and debug on my devices was instantly gratifying, but also revealed another issue: the need to deal with different screen sizes (iPad vs. iPhone) and aspect ratios.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Artist Wanted&lt;/h2&gt;
&lt;div&gt;
It was time to start finalizing the art--getting real assets at the necessary sizes and building out the art style. For this, I&#39;d actually need an artist. Fortunately, Molten&#39;s UI artist was on-board with the project after seeing the prototype running on my phone. He had free reign with the art style and created a look inspired by the likes of &lt;a href=&quot;http://en.wikipedia.org/wiki/Geometry_Wars&quot;&gt;Geometry Wars&lt;/a&gt;&amp;nbsp;yet unique in its own right. We worked together to integrate the art into the game and iterate on the style. Unfortunately, &lt;a href=&quot;http://www.polygon.com/2014/4/8/5593466/molten-games-former-producer-eulogizes-studio&quot;&gt;Molten Games collapsed&lt;/a&gt;&amp;nbsp;around this time, towards the end of March. Priorities shifted to finding a new day job since this project was a journey of Experience and less of Business.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
To Test or not to Test&lt;/h2&gt;
&lt;div&gt;
Working with an artist brought to light another need that I&#39;d have to figure out: distributing test builds of my app to other people. For this I began using &lt;a href=&quot;http://testflightapp.com/&quot;&gt;Test Flight&lt;/a&gt;. This provided a great way to bring testers on board and get feedback. The only issue was minor: when a new device was registered, I would have to add the device&#39;s unique identifier to the provisioning profile and upload another build of the app. I started acquiring testers with a broad spectrum of devices. I also acquired a few devices of my own: non-Retina iPad and older iPhone. I was also blessed with winning an iPad Mini in a contest! I made sure to also test on the newer 64-bit devices. Through this I actually discovered some 64-bit compatibility issues with Cocos2D 3.0 (in beta at the time) and worked with the developers to solve them.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Sprechen Sie Deutsche?&lt;/h2&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnDtFrPN2YTg5derV7SZFyIOhKMG7Lx2FeaYq7G_Ykz1qzSZOUzrqN0SoUhITSYvuG1B2bWb8QUEZQnNRes-28vy-WICqFjxbEMfr3x0Vjq4hRb_yvXbR2RyZL4dbHBoEzbdZyumaorg/s1600/BAM+i18n.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnDtFrPN2YTg5derV7SZFyIOhKMG7Lx2FeaYq7G_Ykz1qzSZOUzrqN0SoUhITSYvuG1B2bWb8QUEZQnNRes-28vy-WICqFjxbEMfr3x0Vjq4hRb_yvXbR2RyZL4dbHBoEzbdZyumaorg/s1600/BAM+i18n.jpg&quot; height=&quot;213&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;I can understand exactly one of these.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;
Having visited Moscow and Shanghai for assisting &lt;a href=&quot;http://planetside2.com/&quot;&gt;Planetside 2&lt;/a&gt; localization (and working on games for the past decade or so), I&#39;m constantly reminded of the worldwide gaming community. The English-speaking community might be easier for me to market, but it&#39;s a small part of the world. Therefore, I wanted to make sure that my app could be distributed and popular in other locales. I had a very small number of strings (less than 100), so professional localization would be fairly easy. I originally started working with &lt;a href=&quot;http://rev.com/&quot;&gt;Rev.com&lt;/a&gt;&amp;nbsp;but found them difficult and obtuse. I ended up going with &lt;a href=&quot;https://e2f.com/&quot;&gt;e2f&lt;/a&gt;&amp;nbsp;and found them slightly more expensive, but very easy to work with. They appreciated all of my contextual notes and turned around six languages (French, Italian, German, Spanish, Russian and Simplified Chinese) in less than 24 hours. In hindsight, I should have included a few more items in the translations, such as future (generic) update notes and anything that I would potentially need in the next few months.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Finishing Touches&lt;/h2&gt;
&lt;div&gt;
This game, since it was an exploration of experience, was going to be free and advertisement-supported. However, given the ease of doing in-app purchases, I would offer an in-app purchase to remove the ads. I also wanted GameKit integration to essentially have free Leaderboards. Fortunately many kind developers have written simple APIs around these pieces of functionality and have shared them for free. Integrating the likes of&amp;nbsp;&lt;a href=&quot;https://github.com/ablfx/ABGameKitHelper&quot;&gt;ABGameKitHelper&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;https://github.com/MugunthKumar/MKStoreKit&quot;&gt;MKStoreKit&lt;/a&gt;&amp;nbsp;was very easily done. My testers also suggesting graphical features such as using UIMotionEffect to simulate a parallax display on iOS 7+.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
A Different Beat&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Having some manner of background music was important to me. However, I&#39;m not a composer. I don&#39;t really know many composers. So I started looking for free music. Interestingly, though, &quot;free&quot; isn&#39;t always free. Some companies let you use their music for free for certain purposes, and an app is not generally one of those free uses. I originally wanted to use some songs from &lt;a href=&quot;http://www.freeplaymusic.com/&quot;&gt;Freeplay Music&lt;/a&gt;, but after seeing their licensing requirements and reading about &lt;a href=&quot;https://www.google.com/search?q=freeplay+music+lawsuit&quot;&gt;their litigation processes&lt;/a&gt;, I decided against it. I ended up using music by &lt;a href=&quot;http://chriszabriskie.com/&quot;&gt;Chris Zabriskie&lt;/a&gt;&amp;nbsp;who offers his music for free with a Creative Commons Attribution license. I feel that his music meshes well with the peaceful, soothing nature of Bust A Mine&#39;s laid-back gameplay.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Pre-Launch Checklist&lt;/h2&gt;
&lt;div&gt;
I&#39;ve heard of using a test market to pre-launch, so I figured I&#39;d give that a try. I have some friends with family in the Philippines and it seems to be a fairly popular test market. Launching in the Philippines also revealed an interesting fact: &lt;a href=&quot;http://advertising.apple.com/&quot;&gt;iAd&lt;/a&gt; isn&#39;t available in all parts of the world. Therefore, my advertisement strategy would have to change for a worldwide launch.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
3... 2... 1... Liftoff!&lt;/h2&gt;
&lt;div&gt;
&lt;a href=&quot;https://itunes.apple.com/app/bust-a-mine/id860372543&quot;&gt;Bust A Mine&lt;/a&gt; officially launched in a limited capacity on 9 May 2014, about 9 weeks after development started. Considering that it was my first foray into iOS development and Objective-C programming in general, I&#39;m calling that a success. Not a business success, but an experiential success. For the full worldwide launch, I&#39;m waiting for Apple to approve the next version that will integrate &lt;a href=&quot;http://www.google.com/ads/admob/&quot;&gt;Google AdMob&lt;/a&gt; as a backup to iAd.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
To Infinity and Beyond&lt;/h2&gt;
&lt;div&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpK0UfxuJ8DnppCwVL7Q14n4j1Vj6jrl8o5Iny-qpJOGkp86HIym378ibXPboRhfcDxHXD_oeTyPniKAepER-pO9M7AEj2uQmlXLEmd-e19SaDsMHM2SUh3feFesr7ufGBAbfP6Lcu6A/s1600/IMG_5452.PNG&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpK0UfxuJ8DnppCwVL7Q14n4j1Vj6jrl8o5Iny-qpJOGkp86HIym378ibXPboRhfcDxHXD_oeTyPniKAepER-pO9M7AEj2uQmlXLEmd-e19SaDsMHM2SUh3feFesr7ufGBAbfP6Lcu6A/s1600/IMG_5452.PNG&quot; height=&quot;180&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;The background reverberates with explosive force&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
I really enjoy iOS development. The Objective-C language allows for easy checking to see if features are available on the device it&#39;s running on. Furthermore, the Xcode integration with Apple back-end services is very well done and easy to use. Want GameKit integration? Flip a switch and you&#39;ve got it.&lt;br /&gt;
&lt;br /&gt;
Several people have asked about an Android port as well. Interestingly, no one has asked about a Windows phone version. In any case, I&#39;m considering a port to &lt;a href=&quot;http://www.cocos2d-x.org/&quot;&gt;Cocos2D-X&lt;/a&gt;, a C++ library that supports all major mobile platforms.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;ve got plenty of ideas for Bust A Mine for the future. And a few of my game designer friends have offered some feedback and ideas of their own. Plus, I have more ideas for future games as well. Mobile development has become a fun little hobby for me.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Download &lt;a href=&quot;https://itunes.apple.com/app/bust-a-mine/id860372543&quot;&gt;Bust A Mine&lt;/a&gt;&amp;nbsp;and give it a try. Leave a review or rating if you feel so inclined. And let me know what you think in the comments!&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2014/05/busting-mines.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg02acLSubcOvYfOmVd0mKBDXkqzjiSSjL3mLX176r4DvnFONb2YaoZ3HX84NJki_A6aTbcLaycBmC1zTvSKFCacyuyM3fNeEyUc1uK-EgsdBRSlDvUTUT98WU2_jCxTo1lBOi71IwXJw/s72-c/bustamine.jpeg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3092484886054086127</guid><pubDate>Wed, 05 Feb 2014 07:11:00 +0000</pubDate><atom:updated>2014-02-04T23:11:50.915-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">fastjson</category><category domain="http://www.blogger.com/atom/ns#">github</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Githubbing</title><description>I just created a repository on github.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/jkriegshauser/fastjson&quot;&gt;https://github.com/jkriegshauser/fastjson&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
For a while I&#39;ve been writing a &lt;a href=&quot;http://www.json.org/&quot;&gt;json&lt;/a&gt; parser (fully &lt;a href=&quot;http://www.ietf.org/rfc/rfc4627.txt&quot;&gt;RFC-4627&lt;/a&gt; compliant) similar in interface to &lt;a href=&quot;http://rapidxml.sourceforge.net/&quot;&gt;rapidxml&lt;/a&gt;&amp;nbsp;as a fun way to write some simple code that ended up being fairly complex. I mean, there&#39;s a good bit of &lt;a href=&quot;http://www.cprogramming.com/tutorial/template_specialization.html&quot;&gt;template partial specialization&lt;/a&gt; in there, exception/non-exception handling, &lt;a href=&quot;http://www.unicode.org/&quot;&gt;Unicode&lt;/a&gt; support, unit-testing, etc.&lt;br /&gt;
&lt;br /&gt;
It even figures out if you&#39;ve given it UTF-8, UTF-16 or UTF-32 and whether the endianness matches your machine or not.&lt;br /&gt;
&lt;br /&gt;
Yeah, so that&#39;s how I have fun. Enjoy, universe!</description><link>http://blog.joshuakriegshauser.com/2014/02/githubbing.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3295285149278068643</guid><pubDate>Fri, 22 Nov 2013 02:46:00 +0000</pubDate><atom:updated>2013-11-23T22:03:35.015-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">games</category><category domain="http://www.blogger.com/atom/ns#">molten</category><category domain="http://www.blogger.com/atom/ns#">work</category><title>Catching Fire</title><description>Well, it happened.&lt;br /&gt;
&lt;br /&gt;
Nearly two months ago I took the plunge and left Sony Online Entertainment to be the Director of Technology at &lt;a href=&quot;http://www.moltengames.com/&quot;&gt;Molten Games&lt;/a&gt;. Molten is a small but well-funded and growing start-up in the San Diego area.&lt;br /&gt;
&lt;br /&gt;
I. Am. Having. A. Blast.&lt;br /&gt;
&lt;br /&gt;
We&#39;re building a rock-star team and making a great game. I can&#39;t wait until I can talk about it, but you&#39;re going to have to stay tuned for a bit.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.moltengames.com/wp-content/uploads/2013/07/MoltenGames_Website_CenterLogo__1373253947_24.234.172.18.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;162&quot; src=&quot;http://www.moltengames.com/wp-content/uploads/2013/07/MoltenGames_Website_CenterLogo__1373253947_24.234.172.18.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;</description><link>http://blog.joshuakriegshauser.com/2013/11/catching-fire.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-2640332405185001068</guid><pubDate>Wed, 09 Jan 2013 07:59:00 +0000</pubDate><atom:updated>2013-01-09T00:00:32.653-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ripping</category><title>Getting Ripped</title><description>For the past few weeks I&#39;ve been converting my DVD and Blu-ray Disc (BD) collection to MP4 format so everything can be easily viewed on the &lt;a href=&quot;http://www.apple.com/appletv/&quot;&gt;Apple TV&lt;/a&gt;. Generally, this has been fairly painless (especially for DVDs), but there is one thing that I desire that most people probably don&#39;t care about: captions.&lt;br /&gt;
&lt;br /&gt;
Maybe I&#39;m getting old, but for about a decade I&#39;ve preferred to watch shows with captions. There are basically three types of captions present on DVD and BD media:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Closed Captions - text-based captions that may include hard-of-hearing information.&lt;/li&gt;
&lt;li&gt;Bitmap Captions - bitmaps that are overlaid on the screen.&lt;/li&gt;
&lt;li&gt;Forced Bitmap Captions - technically a subset of (2). Typically shown for foreign languages.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Bitmap Captions allow the DVD/BD publisher to ensure that captions look the same on every player, whereas text-based Closed Captions can look and perform differently on different players, TVs, etc. And in my experience, Bitmap Captions don&#39;t display on the Apple TV whereas Closed Captions will. Also of note is that DVD and BD each use a separate type of Bitmap Captions--VobSub for DVD and PGS for BD.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Now, if Bitmap Captions don&#39;t show on the Apple TV, why do I care about them? There are two main reasons: 1). If Closed Captions don&#39;t exist and 2). Forced Subtitles.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
At this point I should mention that I&#39;m using &lt;a href=&quot;http://handbrake.fr/&quot;&gt;Handbrake&lt;/a&gt; to encode my movies for the Apple TV. &lt;a href=&quot;http://lifehacker.com/5773000/how-to-rip-dvds-with-handbrake&quot;&gt;Several&lt;/a&gt; &lt;a href=&quot;http://www.pcworld.com/article/242876/how_to_rip_a_dvd_with_handbrake.html&quot;&gt;pages&lt;/a&gt; &lt;a href=&quot;http://www.dvd-guides.com/guides/blu-ray-rip/259-rip-blu-ray-to-mp4-using-handbrake&quot;&gt;exist&lt;/a&gt; as guides to using Handbrake, so I&#39;m not going to cover it in much detail, except for dealing with captions.&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
If Closed Captions Don&#39;t Exist&lt;/h3&gt;
&lt;div&gt;
If a DVD has Bitmap Captions (VobSub) but no Closed Caption, then we need to find a way to add text-based Closed Captions. Fortunately, there are a few options.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The first option is my least favorite. Handbrake has the ability to &lt;i&gt;burn-in&lt;/i&gt;&amp;nbsp;Bitmap Captions. This means that captions will be present, but there will be &lt;i&gt;no way&lt;/i&gt;&amp;nbsp;to turn them off. For all captions, that&#39;s not desirable, but as seen below it can be quite handy for Forced Captions.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUmH4QyuAViTbNdTZZkcutIuxcCcsvNaiDSWyHnzlb6LBBgNzdU8C_Np1DWdvVj3ZYQmZ0VwWAdzL8LHQ-fFv1ahhL0WQWKsJIUezCfCmiaqQ1lt4Hjn9SwSrY0Iv-i7PqJTaFFQ-7Bg/s1600/burned+in.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;75&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUmH4QyuAViTbNdTZZkcutIuxcCcsvNaiDSWyHnzlb6LBBgNzdU8C_Np1DWdvVj3ZYQmZ0VwWAdzL8LHQ-fFv1ahhL0WQWKsJIUezCfCmiaqQ1lt4Hjn9SwSrY0Iv-i7PqJTaFFQ-7Bg/s400/burned+in.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The second option is to find a SubRipper (SRT) file that someone has created. Fortunately there are some great crowd-sourced resources for this, like &lt;a href=&quot;http://www.opensubtitles.org/&quot;&gt;Open Subtitles&lt;/a&gt;&amp;nbsp;and my personal favorite, &lt;a href=&quot;http://subscene.com/&quot;&gt;SubScene&lt;/a&gt;. These sites allow you to search for a SRT file that someone has created and uploaded. Handbrake can import the SRT file by using the Import SRT button (seen in the picture above). The downside to this method is that timing is everything. There can be Special Editions and Director&#39;s Cut versions of the movie you&#39;re searching for, so ensuring that you have the correct file will involve using Handbrake&#39;s Preview feature or using a player (like &lt;a href=&quot;http://www.videolan.org/vlc/index.html&quot;&gt;VLC Media Player&lt;/a&gt;) to preview before encoding. Otherwise you could end up with captions that don&#39;t match.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The final option is to create your own SRT file from the Bitmap Caption. This can either be done manually (VERY time consuming) or with the aid of &lt;a href=&quot;http://en.wikipedia.org/wiki/Optical_character_recognition&quot;&gt;OCR software&lt;/a&gt;. This was more of a last resort for me, but I did do it for a few of my DVDs. I used a program called &lt;a href=&quot;http://www.sub-talk.net/topic/921-converting-idxsub-files-to-srt-with-subresync/&quot;&gt;Subresync&lt;/a&gt;. It is about as naive as OCR software can be, essentially asking the user the first time it encounters a specific pixel pattern:&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP6zkzNBJGRMnEqgzk9tPyMAm5HIvCuN5bX98rVNsRa84o707d3Gc03m4_48jRLNPlAyfjR-QYOdE3Lfp67l2J5MuOQP1h4geniaKRasKy3bzeMVD2b8hwG6hm-5q9hUsdR2eCdmJ9Ng/s1600/subresync.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;216&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP6zkzNBJGRMnEqgzk9tPyMAm5HIvCuN5bX98rVNsRa84o707d3Gc03m4_48jRLNPlAyfjR-QYOdE3Lfp67l2J5MuOQP1h4geniaKRasKy3bzeMVD2b8hwG6hm-5q9hUsdR2eCdmJ9Ng/s320/subresync.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Forced Captions&lt;/h3&gt;
&lt;div&gt;
This is where things get a bit trickier. Forced captions were typically not a problem for DVDs as all of my DVDs that used Forced Captions actually had them burned in to the video stream. Not so for my BDs. A good example is Star Trek VI: The Undiscovered Country where the Klingon speech is a Forced Caption track and is not burned in to the video stream. And Forced Captions are something that everyone should want as they are essentially part of the movie.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Also complicating this process is the fact that Handbrake does not yet understand PGS-style Bitmap Captions (the system used for BDs). Therefore, the PGS (or &#39;Sup&#39;) Captions must be converted to VobSub before using Handbrake. This involves the following steps:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;I use &lt;a href=&quot;http://makemkv.com/&quot;&gt;MakeMKV&lt;/a&gt; to get the BD data on the hard disk in &lt;a href=&quot;http://en.wikipedia.org/wiki/Matroska&quot;&gt;MKV&lt;/a&gt; format.&lt;/li&gt;
&lt;li&gt;Load the .mkv file in the mkvmerge tool from &lt;a href=&quot;http://www.bunkus.org/videotools/mkvtoolnix/&quot;&gt;MKVToolNix&lt;/a&gt;. This will tell you which tracks contain PGS Caption data:&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdYCNfYgbJn88wtrzuF34Ng6Ln1gVVjkmNRKpUjeR2UZ0ObY4L-e1-x50gAsY4mfsYV4dW1ZyVBvF9jUcnyHYpX0CCh2M2o3rqrW7OzvXB0_DX30Y7jOQ44KQYQ-WBs4tl8Q3xohfFrQ/s1600/mkvextract.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;115&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdYCNfYgbJn88wtrzuF34Ng6Ln1gVVjkmNRKpUjeR2UZ0ObY4L-e1-x50gAsY4mfsYV4dW1ZyVBvF9jUcnyHYpX0CCh2M2o3rqrW7OzvXB0_DX30Y7jOQ44KQYQ-WBs4tl8Q3xohfFrQ/s400/mkvextract.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;br /&gt;In this example, IDs 2, 3, 4 and 5 contain PGS subtitle data. Leave this program open as we&#39;ll come back to it if we do indeed find Forced Captions.&lt;/li&gt;
&lt;li&gt;Extract the subtitle data using &lt;a href=&quot;http://www.videohelp.com/tools/MKVExtractGUI&quot;&gt;mkvextract&lt;/a&gt;&amp;nbsp;on the command line:&lt;br /&gt;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;mkvextract tracks Star_Trek_VI...mkv 2:sub2.sup 3:sub3.sup 4:sub4.sup 5:sub5.sup&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;This will extract tracks 2-5 into files sub2.sup, sub3.sup, sub4.sup and sub5.sup&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Open each extracted PGS Subtitle file using &lt;a href=&quot;https://github.com/mjuhasz/BDSup2Sub&quot;&gt;BDSup2Sub&lt;/a&gt; to see if it contains Forced Captions. In the Star Trek 6 example, we find that track 3 contains nothing but Forced Captions:&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbRIfM93AkyqgkSspz5Yf0WgbWmTPaw8wFBrUgBb0WJaffduBIKFtHNbotWjPqbV3EaEVbis4y9u9M4aHVKAxgDeDEuzRbED_NAFpWrd5_646rvTsGjBmS-Sf3SFFJcopXNA-t_EWy1Q/s1600/bdsup2sub.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;226&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbRIfM93AkyqgkSspz5Yf0WgbWmTPaw8wFBrUgBb0WJaffduBIKFtHNbotWjPqbV3EaEVbis4y9u9M4aHVKAxgDeDEuzRbED_NAFpWrd5_646rvTsGjBmS-Sf3SFFJcopXNA-t_EWy1Q/s400/bdsup2sub.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Since we&#39;ve found Forced Captions, we need to convert them to a format that Handbrake understands. Fortunately, this is what BDSup2Sub was designed to do. File-&amp;gt;Export gives the following dialog (make sure that &#39;Export only forced&#39; is checked):&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJo9x5SIOnznQWxyyddW6Fh_QOYf_CQxoj0HFlCvyb4S9K7IadNN4pPCooPA5Xx05Z-zmxnOlIrJZdGruR_HspuAxCT-fa_mLg-B9prriyXSuLGixsmW2igX7C69YgLkLCQ_95b604ww/s1600/bdsup2sub2.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;102&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJo9x5SIOnznQWxyyddW6Fh_QOYf_CQxoj0HFlCvyb4S9K7IadNN4pPCooPA5Xx05Z-zmxnOlIrJZdGruR_HspuAxCT-fa_mLg-B9prriyXSuLGixsmW2igX7C69YgLkLCQ_95b604ww/s200/bdsup2sub2.PNG&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Interestingly, VobSub is made up of a .idx text file and a .sub binary file.&lt;/li&gt;
&lt;li&gt;Now we need to add the newly created VobSub captions and re-mux into a new .mkv file. Go back to the mkvmerge that was open from step 2. Click the Add button and select the new .idx file that was created in step 5, then press Start Muxing:&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3aeTxHALlKI_rtOmk0rl8IzRqcNXBozyIpkO-KqveqFgRWPlPsj5pbRfEQ85zBtxTesLwiY5avdHXc298O3driDd8iViBzX4Bxc2IquCi60MUycIbsZxGVI06nsgO5BCyyKCV4evjCg/s1600/mkvmerge.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;310&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3aeTxHALlKI_rtOmk0rl8IzRqcNXBozyIpkO-KqveqFgRWPlPsj5pbRfEQ85zBtxTesLwiY5avdHXc298O3driDd8iViBzX4Bxc2IquCi60MUycIbsZxGVI06nsgO5BCyyKCV4evjCg/s320/mkvmerge.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;When the muxing finishes, you can now load the new .mkv file up in Handbrake and burn-in the Forced Captions:&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKZbkMnIdEpXQG9xAUCfgMvI7kXroAT33CVchiK0fd4vqGhvlZBJr6_2N6Ca15BkSvw2IoejM8m_V-LV1hB9ef-6CKVZlT-1IYadrxVPDr7VhQPXHQFkb8ezlFds7D9wRKII5OWV_pkg/s1600/handbrake.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;194&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKZbkMnIdEpXQG9xAUCfgMvI7kXroAT33CVchiK0fd4vqGhvlZBJr6_2N6Ca15BkSvw2IoejM8m_V-LV1hB9ef-6CKVZlT-1IYadrxVPDr7VhQPXHQFkb8ezlFds7D9wRKII5OWV_pkg/s320/handbrake.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.youtube.com/watch?v=1v0emSvJPGE&quot;&gt;Qapla&#39;!&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHsEWPp3GgyAz87qgwCKaDLo_kmc_W0wR77jI0Es55Rv2aIuk7V_vyP0bF6cBsDU_hDpqz6jqtKt91yCdAFad-7P7dSUE4_Z5cC3hXAz4o7ZrllyKEN3Fj5r92LwLnkPXiuo1IenmGRQ/s1600/success.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;216&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHsEWPp3GgyAz87qgwCKaDLo_kmc_W0wR77jI0Es55Rv2aIuk7V_vyP0bF6cBsDU_hDpqz6jqtKt91yCdAFad-7P7dSUE4_Z5cC3hXAz4o7ZrllyKEN3Fj5r92LwLnkPXiuo1IenmGRQ/s320/success.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
Getting captions to work correctly on the Apple TV with custom-encoded movies can be a bit tricky, but if you&#39;re hard-of-hearing or just used to using captions, hopefully this guide can help you.&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2013/01/getting-ripped.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUmH4QyuAViTbNdTZZkcutIuxcCcsvNaiDSWyHnzlb6LBBgNzdU8C_Np1DWdvVj3ZYQmZ0VwWAdzL8LHQ-fFv1ahhL0WQWKsJIUezCfCmiaqQ1lt4Hjn9SwSrY0Iv-i7PqJTaFFQ-7Bg/s72-c/burned+in.PNG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-2073516442954434254</guid><pubDate>Wed, 02 May 2012 19:17:00 +0000</pubDate><atom:updated>2012-05-02T12:17:40.393-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">web</category><title>New Domain!</title><description>Well, I&#39;ve finally gotten around to registering &lt;a href=&quot;http://joshuakriegshauser.com/&quot;&gt;joshuakriegshauser.com&lt;/a&gt;. It&#39;s about time. Which means that this blog will appear at &lt;a href=&quot;http://blog.joshuakriegshauser.com/&quot;&gt;blog.joshuakriegshauser.com&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Oddly enough, kriegshauser.com is considered a &lt;i&gt;premium&lt;/i&gt; domain and is listed for about $1600. I honestly hope whoever is holding it never gets that.</description><link>http://blog.joshuakriegshauser.com/2012/05/new-domain.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-5438437953410185370</guid><pubDate>Fri, 06 Jan 2012 06:03:00 +0000</pubDate><atom:updated>2013-01-03T10:51:02.048-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">solvers</category><title>1 in 600,000</title><description>&lt;div style=&quot;text-align: left;&quot;&gt;
For Christmas this year, my wife gave me (among other things) &lt;a href=&quot;http://www.amazon.com/Great-American-Puzzle-Factory-GA135/dp/B0052WBHXU/ref=cm_cr_pr_product_top&quot; style=&quot;text-align: left;&quot;&gt;Another Tough Puzzle&lt;/a&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;text-align: left;&quot;&gt;. This puzzle claims that &quot;with millions of wrong solutions, the chance of solving this puzzle is ONE in 600,000!&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
There are 16 triangular pieces and each piece has three connections each being one of four possible shapes. The completed puzzle should be a pyramid with six connections on the edges pointing in and six pointed out.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The puzzle also claims that &quot;it looks easy...but just when you think you&#39;ve solved it, the last piece proves you wrong.&quot; This is very true and very frustrating. I (and others) played around with the puzzle for a while always ending up with one piece that didn&#39;t fit.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Then I thought, &quot;I write software for a living; why don&#39;t I do that here?&quot; I&#39;ve enjoyed writing solvers in the past (my Minesweeper solver would actually play the GUI for you on previous Windows versions). I mostly use C++, so I decided to use it here as well. Certainly there are languages more suited to rapid development, but I&#39;m a glutton for punishment.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The first step was to describe the pieces. We noticed pretty quickly that half of the pieces have two outward-facing connectors (outputs) and one inward-facing connector (input) and the other half are opposite (two inputs and one output). There are no pieces with three inputs or outputs.&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;enum FaceType
{
    eFaceTypeClover = 0,
    eFaceTypeHeart,
    eFaceTypeSpade,
    eFaceTypeDiamond,
};

enum Direction
{
    eDirIn = 0,
    eDirOut,
};

struct Node
{
    FaceType face;
    Direction dir;

    bool fits(const Node&amp;amp; rhs) const
    {
        return face == rhs.face &amp;amp;&amp;amp; dir != rhs.dir;
    }
};
struct Piece
{
    Node node[eNumSides];

    Piece(FaceType in, FaceType out1, FaceType out2, bool)
    {
        node[0].face = in;
        node[1].face = out1;
        node[2].face = out2;
        node[0].dir = eDirIn;
        node[1].dir = node[2].dir = eDirOut;
    }
    Piece(FaceType in1, FaceType in2, FaceType out)
    {
        node[0].face = in1;
        node[1].face = in2;
        node[2].face = out;
        node[0].dir = node[1].dir = eDirIn;
        node[2].dir = eDirOut;
    }
};&lt;/pre&gt;
Using these structures, all of the pieces can be declared:&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;Piece pieces[eNumPieces] =
{
// one input, two outputs
Piece(eFaceTypeSpade, eFaceTypeDiamond, eFaceTypeHeart),        // 0
Piece(eFaceTypeHeart, eFaceTypeDiamond, eFaceTypeSpade),        // 1
Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeSpade),       // 2
Piece(eFaceTypeClover, eFaceTypeSpade, eFaceTypeHeart),         // 3
Piece(eFaceTypeSpade, eFaceTypeHeart, eFaceTypeClover),         // 4
Piece(eFaceTypeHeart, eFaceTypeSpade, eFaceTypeClover),         // 5
Piece(eFaceTypeSpade, eFaceTypeClover, eFaceTypeDiamond),       // 6
Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeHeart),       // 7

// two inputs, one output
Piece(eFaceTypeClover, eFaceTypeDiamond, eFaceTypeHeart, true), // 8
Piece(eFaceTypeClover, eFaceTypeDiamond, eFaceTypeSpade, true), // 9
Piece(eFaceTypeHeart, eFaceTypeDiamond, eFaceTypeSpade, true),  // a
Piece(eFaceTypeHeart, eFaceTypeClover, eFaceTypeDiamond, true), // b
Piece(eFaceTypeHeart, eFaceTypeClover, eFaceTypeSpade, true),   // c
Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeHeart, true), // d
Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeSpade, true), // e
Piece(eFaceTypeSpade, eFaceTypeHeart, eFaceTypeDiamond, true)   // f
};&lt;/pre&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMjmLAZOU5PoC2ZzH7C-R9zOccmyHpqzu-4Qb_H89Fszr3JqxwctZBKd7gMB7LhVWdgZRnEkCW10GBI9iS3oz5MpQ7w4HfwXMx66SVPCnsLP1UwdI16C4lC2lW-_cSTpWCIq9PrmLThQ/s1600/gameboard.png&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5696631321866827346&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMjmLAZOU5PoC2ZzH7C-R9zOccmyHpqzu-4Qb_H89Fszr3JqxwctZBKd7gMB7LhVWdgZRnEkCW10GBI9iS3oz5MpQ7w4HfwXMx66SVPCnsLP1UwdI16C4lC2lW-_cSTpWCIq9PrmLThQ/s400/gameboard.png&quot; style=&quot;cursor: pointer; float: left; height: 298px; margin-bottom: 10px; margin-left: 0px; margin-right: 10px; margin-top: 0px; width: 254px;&quot; /&gt;&lt;/a&gt;Now that the pieces are all set up, the gameboard must be described. The end result should be a pyramid made up of all 16 pieces (see diagram). Therefore, I had to describe how the pieces relate to each other in the gameboard. This will allow the solver algorithm to test that a piece &#39;fits&#39; if its connectors match the adjacent pieces. In order to do this, each position in the desired puzzle shape is marked with a number and each side of the triangular pieces are numbered as well. The sides are always numbered clockwise starting at the horizontal side so that adjacent pieces in the puzzle have sides matching (for instance, pieces 7 and 8 both share their side number 2).&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The array of PieceLoc structures define how each piece relates to adjacent pieces:&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;PieceLoc locs[eNumPieces] =
{
// Sides:     0               1               2          // Pieces
PieceLoc(eInvalidPiece,  eInvalidPiece,              1), // 0
PieceLoc(            7,              2,              0), // 1
PieceLoc(eInvalidPiece,              1,              3), // 2
PieceLoc(            9,              4,              2), // 3
PieceLoc(eInvalidPiece,              3,              5), // 4
PieceLoc(           11,              6,              4), // 5
PieceLoc(eInvalidPiece,              5,  eInvalidPiece), // 6
PieceLoc(            1,  eInvalidPiece,              8), // 7
PieceLoc(           12,              9,              7), // 8
PieceLoc(            3,              8,             10), // 9
PieceLoc(           14,             11,              9), // a
PieceLoc(            5,             10,  eInvalidPiece), // b
PieceLoc(            8,  eInvalidPiece,             13), // c
PieceLoc(           15,             14,             12), // d
PieceLoc(           10,             13,  eInvalidPiece), // e
PieceLoc(           13,  eInvalidPiece,  eInvalidPiece)  // f
};&lt;/pre&gt;
The fitting function is recursive. The first time it is called it has all 16 pieces remaining to try and fit. It loops through all remaining pieces rotating them in all possible positions each time checking to see if the piece fits. If the piece fits, it calls itself again this time with 15 pieces remaining. When a pieces is found that fits, it calls itself again with 14 pieces remaining. And so on, and so forth until all pieces have been used. If a piece doesn&#39;t fit, it goes on to the next piece. If a solution is found or no more pieces fit, the function returns and tries the next piece.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Since we only have 16 pieces, I use a bitmask to determine which pieces are already used.&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;void FitRemaining(int remain)
{
    if (remain &amp;lt;= 0)
    {
        ShowSolution();
    }
    else
    {
        int index = eNumPieces-remain;
        for (int i = 0; i &amp;lt; eNumPieces; ++i)
        {
            if ((used &amp;amp; (1 &amp;lt;&amp;lt; i)) == 0)
            {
                used |= (1 &amp;lt;&amp;lt; i); // Mark in-use
                pieces[index] = i;
                for (int r = 0; r &amp;lt; eNumRotations; ++r)
                {
                    rotation[index] = r;
                    if (validate(index))
                    {
                        FitRemaining(remain - 1);
                    }
                }
                pieces[index] = eInvalidPiece;
                used &amp;amp;= ~(1 &amp;lt;&amp;lt; i); // No longer in-use
            }
        }
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
The last part is the validation function. We really only need to validate the most recent piece added. Figuring out which sides are facing each other is the most difficult part, but even that is pretty easy. Because of how the gameboard is organized we already know that side indexes on adjacent pieces always face each other. All we need to do is take the rotation into account and ensure that the shapes match:&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;bool validate(int piece) const
{
    assert(piece &amp;gt;= 0 &amp;amp;&amp;amp; piece &amp;lt; eNumPieces);
    const Piece* cur = Get(piece);
    int rot1 = rotation[piece];
    const PieceLoc&amp;amp; fit = locs[piece];
    for (int f = 0; f &amp;lt; eNumSides; ++f)
    {
        const Piece* p = Get(fit.neighbors[f]);
        if (p)
        {
            int rot2 = rotation[fit.neighbors[f]];
            if (!cur-&amp;gt;node[(rot1 + f) % 3].fits(p-&amp;gt;node[(rot2 + f) % 3]))
            {
                return false;
            }
        }
    }
    return true;
}&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
That&#39;s all there is to it! I probably spent less time writing this program than manually (and frustratingly) trying to find the solution.&lt;br /&gt;
&lt;br /&gt;
In fact with this program I found all 518,469 solutions to the puzzle. Here&#39;s the first one:&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEcuSfzNTUJXfJqH5V4_3JVSO2b9MFds-2jcJvqI0Nwf_WLLt9BTnsJ0KCFFZIfP9HoGkL717R9Y41-_bAWyv9SBpXABoWdOU9_8Af7J0MFDRLGSVZWFKDqONbyQAnbRoIzQ7CDq7Wlw/s1600/puzzle.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5694399814727070562&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEcuSfzNTUJXfJqH5V4_3JVSO2b9MFds-2jcJvqI0Nwf_WLLt9BTnsJ0KCFFZIfP9HoGkL717R9Y41-_bAWyv9SBpXABoWdOU9_8Af7J0MFDRLGSVZWFKDqONbyQAnbRoIzQ7CDq7Wlw/s400/puzzle.jpg&quot; style=&quot;cursor: pointer; display: block; height: 299px; margin-bottom: 10px; margin-left: auto; margin-right: auto; margin-top: 0px; text-align: center; width: 400px;&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
If you&#39;d like the full C++ source: &lt;a href=&quot;https://www.dropbox.com/s/2vblk6uduv3lvv9/PuzzleSolver.zip&quot;&gt;Go here&lt;/a&gt;.&lt;/div&gt;
</description><link>http://blog.joshuakriegshauser.com/2012/01/1-in-600000.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMjmLAZOU5PoC2ZzH7C-R9zOccmyHpqzu-4Qb_H89Fszr3JqxwctZBKd7gMB7LhVWdgZRnEkCW10GBI9iS3oz5MpQ7w4HfwXMx66SVPCnsLP1UwdI16C4lC2lW-_cSTpWCIq9PrmLThQ/s72-c/gameboard.png" height="72" width="72"/><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4660979636036289593</guid><pubDate>Thu, 21 Jul 2011 21:44:00 +0000</pubDate><atom:updated>2011-07-21T15:00:54.340-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">conferences</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><title>GDC Online 2011</title><description>I haven&#39;t done a speaking engagement since &lt;a href=&quot;http://www.loginconference.com/&quot;&gt;LOGIN&lt;/a&gt; 2009, but I really enjoyed it, so I figured it was high time to give it another shot.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;(And no, acting out Beyoncé&#39;s &lt;a href=&quot;http://www.youtube.com/watch?v=4m1EFMoRFvY&quot;&gt;Single Ladies dance&lt;/a&gt; on stage at the Developer War Stories panel of &lt;a href=&quot;http://soefanfaire.com/&quot;&gt;Fan Faire 2011&lt;/a&gt; doesn&#39;t count.)&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I&#39;ve always enjoyed attending &lt;a href=&quot;http://gdconline.com/&quot;&gt;GDC Online&lt;/a&gt; (formerly GDC Austin or Austin Game Developers&#39; Conference) and since it has always catered to a large crowd of &lt;i&gt;online&lt;/i&gt; game developers (and I do miss Austin a bit), it&#39;s as good as any a place to foist my shenanigans on the somnolent audience.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This year I&#39;ll be talking about the trials and tribulations of taking a square peg never designed for streaming, &lt;a href=&quot;http://everquest2.com/&quot;&gt;EverQuest II&lt;/a&gt; and inserting it into a round hole. If you&#39;ve read this blog, I&#39;ve &lt;a href=&quot;http://kriegshauser.blogspot.com/search/label/eq2&quot;&gt;mentioned things&lt;/a&gt; about this before, but this is your opportunity to see the veil ripped away and the nitty-gritty of the good and bad choices that were made.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you&#39;re heading to GDC Online (and you should for this lecture alone!), &lt;a href=&quot;http://schedule.gdconline.com/session/5916/EverQuest_II_Extended%3A_Streaming_a_Non-Streaming_Game&quot;&gt;click here&lt;/a&gt; to see the lecture in their Schedule Builder.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Also (and completely to my surprise), Gamasutra &lt;a href=&quot;http://www.gamasutra.com/view/news/35344/GDC_Online_2011_Reveals_Riot_Games_EverQuest_II_Gamification_Talks.php&quot;&gt;listed my lecture&lt;/a&gt; as a &#39;highlight&#39; of GDC Online. Fortunately they don&#39;t know me that well yet. ;D&lt;/div&gt;</description><link>http://blog.joshuakriegshauser.com/2011/07/gdc-online-2011.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1888133255660618139</guid><pubDate>Fri, 01 Apr 2011 01:09:00 +0000</pubDate><atom:updated>2011-03-31T23:40:53.845-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Philosophy of Code</title><description>&lt;div&gt;Programming in a business environment requires creating a product or service that is intended to produce a higher profit than cost. However, I believe that few programmers understand this or think about it on a daily basis. At the end of the day, we must be working towards making a profit.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I&#39;ll be the first to tell you that I&#39;m not a business major. I&#39;m not an economist. I love programming and was best friends with a keyboard in the third grade. I can write code like the dickens but I&#39;m no Wizard of Business. However, throughout the past year working on &lt;a href=&quot;http://www.clonewarsadventures.com/&quot;&gt;Clone Wars Adventures&lt;/a&gt; I&#39;ve had time (while my &lt;a href=&quot;http://xkcd.com/303/&quot;&gt;code is compiling&lt;/a&gt;) and inclination to reflect on the nature of programming in a business environment.&lt;br /&gt;&lt;br /&gt;I&#39;ve basically come to the conclusion that programming is a balance of three distinct yet intertwined facets: Developer Efficiency, Code Correctness and Performance. I call this the Philosophy of Code:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitJWa3xGpBWFDIOFJTLm_thwU2Q7sh1DPO6022FkxrRqZP_JicHs88YKCjeJBYl0KDhtKWxMyzl1UmzbzVt28MjX_BaxqGp6UnQgrXcSL2nRenvmn0VllwBRaka45a5BRBbqiq8U9qJg/s1600/Trifecta.PNG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 392px; height: 340px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitJWa3xGpBWFDIOFJTLm_thwU2Q7sh1DPO6022FkxrRqZP_JicHs88YKCjeJBYl0KDhtKWxMyzl1UmzbzVt28MjX_BaxqGp6UnQgrXcSL2nRenvmn0VllwBRaka45a5BRBbqiq8U9qJg/s400/Trifecta.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5590416217972018962&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Performance &lt;/span&gt;- How quickly the code executes.&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Developer Efficiency&lt;/span&gt; - How quickly developers are able to complete tasks.&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Code Correctness&lt;/span&gt; - Adherence to best practices (avoiding global memory, avoiding macros, namespaces, templates, etc.).&lt;br /&gt;&lt;br /&gt;These three aspects are not necessarily mutually exclusive. Every programming task that we work on has some balance of the above. However, when evaluating this philosophy from a business perspective, I&#39;ve come to believe that the most important element of the triad is &lt;span style=&quot;font-style: italic;&quot;&gt;Developer Efficiency&lt;/span&gt;. Developing a Philosophy of Code that puts emphasis on Developer Efficiency will allow your programmers (and incidentally your entire development team) to work faster and smarter. It can also require less code to be written and fewer bugs.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Mutual Exclusivity&lt;/span&gt;&lt;br /&gt;In some cases, an Efficiency-based Philosophy of Code will compromise the Performance and Code Correctness aspects. For instance, &lt;a href=&quot;http://www.everquest2.com/&quot;&gt;EverQuest II&lt;/a&gt; has a console-variable system. With &lt;span style=&quot;font-weight: bold;&quot;&gt;one line of code&lt;/span&gt; in an implementation file I can add a semi-constant: a named value that can be changed by typing a slash-command into the chat window:&lt;br /&gt;&lt;pre&gt;CV_FLOAT(max_radius, 30.0);&lt;/pre&gt;Presto. This is like saying &lt;code&gt;float max_radius = 30.0;&lt;/code&gt; but I can also change it in real-time by typing &quot;/max_radius 25&quot; into the chat window. This typically is frowned upon from Code Correctness aspects for a few reasons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It uses macros&lt;/li&gt;&lt;li&gt;It uses global memory&lt;/li&gt;&lt;li&gt;It causes code to execute before &lt;code&gt;main()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;If your Philosophy of Code leans towards Code Correctness, doing it the Right Way™ would entail at least:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Creating a manager object as a member of your application manager class&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Writing explicit code to register a member of your class with the manager object&lt;/li&gt;&lt;li&gt;Writing explicit code to unregister that member when your class instance is destroyed&lt;/li&gt;&lt;li&gt;A lot more than one line of code&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Leaning towards Code Correctness over Developer Efficiency in this case would require that your programmers spend more time writing code and compiling (since members are typically declared in header files) to accomplish a very simple task.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Compile Times&lt;/span&gt;&lt;br /&gt;In some cases, Code Correctness philosophies can institute policies that work to the detriment of Developer Efficiency:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Extensive use of templates&lt;/li&gt;&lt;li&gt;Excessive class declarations&lt;/li&gt;&lt;li&gt;Lack of forward declarations&lt;/li&gt;&lt;/ul&gt;Excessive templatization typically means that header files are included in more places and more code is written in header files. When header files are changed, they require more of the code to be rebuilt. More code written in header files can also contribute to longer link times.&lt;br /&gt;&lt;br /&gt;Time spent compiling and linking can&#39;t necessarily be taken at face value. As programmers wait for a rebuild they tend to do something else which probably takes longer than the rebuild. The shorter the rebuild time, the more likely the programmer is going to stay focused. From a Developer Efficiency perspective, it&#39;s in your best interest to keep compile times as low as possible.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Code Bloat&lt;/span&gt;&lt;br /&gt;The less code that is required to perform a task, the more efficiently a developer can implement said code. I&#39;ll give another practical example from EverQuest II. I&#39;ve &lt;a href=&quot;http://kriegshauser.blogspot.com/2009/10/keep-designer-data-in-files-not.html&quot;&gt;previously mentioned&lt;/a&gt; a bit about how the designer data system works. Every server-side data file uses the same data format.  As such, no special code is required to load each type of file. Weapons, quests, characters, everything is all defined by the same generic data description language. To load data from any data file uses a simple interface:&lt;br /&gt;&lt;pre&gt;DataObject* pObject = DataLoader::Load(&quot;weapons/sword_of_awesomesauce&quot;);&lt;br /&gt;if (pObject-&amp;gt;IsA(&quot;Weapon&quot;))&lt;br /&gt;{&lt;br /&gt;  String name = pObject-&amp;gt;GetField(&quot;Name&quot;).AsString();&lt;br /&gt;}&lt;/pre&gt;Due to data object inheritance, &lt;code&gt;pObject&lt;/code&gt; may &lt;span style=&quot;font-style: italic;&quot;&gt;actually&lt;/span&gt; be a Sword, but inherits from Weapon. The server&#39;s object model is further generified so that instantiating any object requires just providing the type:&lt;br /&gt;&lt;pre&gt;Item* pItem = ObjectFactory::Spawn(Item::Type, &quot;weapons/sword_of_awesomesauce&quot;);&lt;br /&gt;if (pItem &amp;amp;&amp;amp; pItem-&amp;gt;IsA(Weapon::Type))&lt;br /&gt;{&lt;br /&gt; // Successfully spawned and is actually a Weapon.&lt;br /&gt;}&lt;/pre&gt;In other words, there is very little code that needs to be written to add a new game object type.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Conclusion&lt;br /&gt;&lt;/span&gt;Programmers should consider their Philosophy of Code. I believe that focusing on Developer Efficiency in a business setting makes the most sense given the deadlines, the complexity of the tasks at hand, and the goal to make a profit.&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;</description><link>http://blog.joshuakriegshauser.com/2011/03/philosophy-of-code.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitJWa3xGpBWFDIOFJTLm_thwU2Q7sh1DPO6022FkxrRqZP_JicHs88YKCjeJBYl0KDhtKWxMyzl1UmzbzVt28MjX_BaxqGp6UnQgrXcSL2nRenvmn0VllwBRaka45a5BRBbqiq8U9qJg/s72-c/Trifecta.PNG" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1961461251101742304</guid><pubDate>Wed, 23 Mar 2011 16:50:00 +0000</pubDate><atom:updated>2011-03-23T13:56:26.460-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Debugging information cannot be found? No Problem!</title><description>In my &lt;a href=&quot;http://kriegshauser.blogspot.com/2011/02/current-bane-of-my-existence.html&quot;&gt;last post&lt;/a&gt;, I discussed the frustrations of Visual Studio 2005 (and later versions) not being able to locate debugging information, even though it was present and fully usable by other tools.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I believe I have discovered a workaround that works nearly all the time: Remote Debugging.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It turns out that running the Remote Debugging Monitor on the same machine and setting up the project to use Remote Debugging seems to work around whatever issue Visual Studio is experiencing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Remote Debugging is really useful if another machine is experiencing a problem that you can&#39;t reproduce locally. The Remote Debugging Monitor is primarily intended to run on a different machine than Visual Studio, but can also run on the same machine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here is how to set up Remote Debugging:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Generally, the machine running the Remote Debugging Monitor will need the files. These are found under the Visual Studio install location: &lt;a href=&quot;file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio%208/Common7/IDE/Remote%20Debugger/x86&quot;&gt;C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\x86&lt;/a&gt; (for 32-bit) or &lt;a href=&quot;file:///C:/Program%20Files/Microsoft%20Visual%20Studio%208/Common7/IDE/Remote%20Debugger/x64&quot;&gt;C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\x64&lt;/a&gt; (for 64-bit).&lt;/li&gt;&lt;li&gt;Copy the files from the above directory to the machine that will run the Remote Debugging Monitor (skip if you are running the Monitor locally).&lt;/li&gt;&lt;li&gt;To start the Remote Debugging Monitor, run msvsmon.exe. If you are running locally, there should be shortcuts in the start menu:&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPf33X7CUAqGEpmwpVH_op9J9DCDv5ncYjyW_wGEaasz6fZgvLaY1X2BOwI8RXiwTkYgal2vJhzLEuDFszyT9eDvYlJsbU1_u1V8bq06MjrQ9PWecHtsFxPSDpCM4vXkkRO2ZpaTBLZQ/s1600/RemoteDebugging1.png&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPf33X7CUAqGEpmwpVH_op9J9DCDv5ncYjyW_wGEaasz6fZgvLaY1X2BOwI8RXiwTkYgal2vJhzLEuDFszyT9eDvYlJsbU1_u1V8bq06MjrQ9PWecHtsFxPSDpCM4vXkkRO2ZpaTBLZQ/s320/RemoteDebugging1.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5587328001516961122&quot; style=&quot;cursor: pointer; width: 320px; height: 311px; &quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Configure the options if necessary. Take note of &#39;Server name&#39;.&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXgVhEYZ_hCaXc4UgGcTmtLYq5Oo6-kv6BUXvYy55CGUMjwrAD8IQhguNjeM0Dzs3C0SQTcXKgmKygqvq5UqJ4dMYp4TnKXKCjinP8cfXMQe0HFpjn7n3J0acojbITFWHJlPrX1ZCtlg/s1600/RemoteDebugging4.png&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXgVhEYZ_hCaXc4UgGcTmtLYq5Oo6-kv6BUXvYy55CGUMjwrAD8IQhguNjeM0Dzs3C0SQTcXKgmKygqvq5UqJ4dMYp4TnKXKCjinP8cfXMQe0HFpjn7n3J0acojbITFWHJlPrX1ZCtlg/s320/RemoteDebugging4.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5587335809456919026&quot; style=&quot;cursor: pointer; width: 320px; height: 167px; &quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI_pNFWrKQjk9zjCJKDumRGZjxDeBFfl2ZOXs8SZX1Sug7KxaRsU24VsaWZKn7QM-2LYJDJPvnAzNxgRe4hb7TFYsmsbx_PuXXAFAzGGnTpwK-7xdO5rhjTiqyaERKbyFPuMqEgAi8xQ/s1600/RemoteDebugging2.png&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI_pNFWrKQjk9zjCJKDumRGZjxDeBFfl2ZOXs8SZX1Sug7KxaRsU24VsaWZKn7QM-2LYJDJPvnAzNxgRe4hb7TFYsmsbx_PuXXAFAzGGnTpwK-7xdO5rhjTiqyaERKbyFPuMqEgAi8xQ/s320/RemoteDebugging2.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5587329706002173442&quot; style=&quot;cursor: pointer; width: 320px; height: 234px; &quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Configure your project to do Remote Debugging. Change the &#39;Debugger to launch&#39; to &#39;Remote Windows Debugger&#39;. The &#39;Remote Server Name&#39; must match the &#39;Server name&#39; configured above if using Windows Authentication. If you are running the Monitor on a remote machine, the Remote Command and Working Directory will be paths on the remote machine.&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8tq4jYfRjCTaUpwrhnjd91LAcU98rDCJekmaS9M_IUAjgImlQrIVsPek-JCR3H3l1XAGDS730D3wc1uiqI_P5dMXWnrvgEL1XQgNvZUUsK_wA6hk0jngFdPxNaXs925qMLI5N77gZww/s1600/RemoteDebugging3.png&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8tq4jYfRjCTaUpwrhnjd91LAcU98rDCJekmaS9M_IUAjgImlQrIVsPek-JCR3H3l1XAGDS730D3wc1uiqI_P5dMXWnrvgEL1XQgNvZUUsK_wA6hk0jngFdPxNaXs925qMLI5N77gZww/s320/RemoteDebugging3.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5587382108402405906&quot; style=&quot;cursor: pointer; width: 320px; height: 222px; &quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;When you Start Debugging, Step Into or Step Over to start debugging the application, Visual Studio will now connect to the Remote Debugging Monitor. You should be able to verify this by looking at the Remote Debugging Monitor and seeing how many connections it has.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So far this appears (for me at least) to be a successful work around to the &#39;Debugging information for &#39;X.exe&#39; cannot be found or does not match. No symbols loaded.&#39; error message.  There have only been about two times in the past month where a problem occurred. In those cases, a restart of Visual Studio (and killing mspdbsrv.exe) fixed it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Please let me know if this worked for you!&lt;/div&gt;</description><link>http://blog.joshuakriegshauser.com/2011/03/debugging-information-cannot-be-found.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPf33X7CUAqGEpmwpVH_op9J9DCDv5ncYjyW_wGEaasz6fZgvLaY1X2BOwI8RXiwTkYgal2vJhzLEuDFszyT9eDvYlJsbU1_u1V8bq06MjrQ9PWecHtsFxPSDpCM4vXkkRO2ZpaTBLZQ/s72-c/RemoteDebugging1.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-7130972975746472925</guid><pubDate>Fri, 04 Feb 2011 01:23:00 +0000</pubDate><atom:updated>2011-03-23T13:58:03.772-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cwa</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>The (current) Bane of my Existence</title><description>&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: rgb(0, 0, 238);&quot;&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/span&gt;&lt;/div&gt;Yes, &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms950416.aspx&quot;&gt;Microsoft Visual Studio 2005&lt;/a&gt;, I&#39;m looking at you.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Whenever I try to run the &lt;a href=&quot;http://www.clonewarsadventures.com/&quot;&gt;Clone Wars Adventures&lt;/a&gt; AdminClient in the debugger, I have about an 80% chance of seeing this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLWfllf7UidpRPeXBywbN7Je3FCgdvCgQeMZ1odAJkWpR_sxcys_zDHtSof2nWZc6G7AyxxGUDE698BUwoySV2csQB2OX52kNCvx9tNx1rXyeXWMK8VptqJ0Bb4fEp1J_blKChZWnWA/s1600/NoDebuggingInfo.JPG&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLWfllf7UidpRPeXBywbN7Je3FCgdvCgQeMZ1odAJkWpR_sxcys_zDHtSof2nWZc6G7AyxxGUDE698BUwoySV2csQB2OX52kNCvx9tNx1rXyeXWMK8VptqJ0Bb4fEp1J_blKChZWnWA/s400/NoDebuggingInfo.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5569639570791674994&quot; style=&quot;display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 124px;&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;This is a debug build.  I most assuredly have debug symbols turned on:&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisKgL1MOVgHFbYXFRlFnRnIE-jrN5MdYbUImW_WK3XNibUKUNtf5mMOtxV1qoollzSk0uCxVnJZCJv2XETi2jN2URrw_8n6RbbngU6mvGkptDL3a-uKMGDkkGk5MYFBOfwWB5pA-IfxA/s1600/DebugSettings2.JPG&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisKgL1MOVgHFbYXFRlFnRnIE-jrN5MdYbUImW_WK3XNibUKUNtf5mMOtxV1qoollzSk0uCxVnJZCJv2XETi2jN2URrw_8n6RbbngU6mvGkptDL3a-uKMGDkkGk5MYFBOfwWB5pA-IfxA/s400/DebugSettings2.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5569640208559447298&quot; style=&quot;display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 280px;&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHm1wmcRrXr3VaEtED0zOxGuWtlYoCkOx4Ph9DRXKyW81_sWObRJXgaIGYmaju8zlc6kKW3ozuZpoRrTgIxe3-wvZbnvTvLwohYNyFPGQWmkMig3N8W9kFCLEc6E9uyPatlIeDMxJ81Q/s1600/DebugSettings1.JPG&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHm1wmcRrXr3VaEtED0zOxGuWtlYoCkOx4Ph9DRXKyW81_sWObRJXgaIGYmaju8zlc6kKW3ozuZpoRrTgIxe3-wvZbnvTvLwohYNyFPGQWmkMig3N8W9kFCLEc6E9uyPatlIeDMxJ81Q/s400/DebugSettings1.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5569640340284230130&quot; style=&quot;display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 280px;&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;And I&#39;m &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/7323de2b-3155-49a6-b4ec-955621eae6d7&quot;&gt;not&lt;/a&gt; &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/4f7b3eb5-67b5-4066-8299-fe7635cc1d82&quot;&gt;the&lt;/a&gt; &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/b1a12f69-5c47-4ec6-a7d8-c1d16c5a710a&quot;&gt;only&lt;/a&gt; &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/07395956-61c5-45ce-b8d0-5c35ef489393&quot;&gt;one&lt;/a&gt; &lt;a href=&quot;http://connect.microsoft.com/VisualStudio/feedback/details/486244/debugging-information-for-exe-cannot-be-found-or-does-not-match&quot;&gt;to&lt;/a&gt; &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvc/thread/503d3ded-976e-460e-b5f9-7e03454ed387&quot;&gt;ever&lt;/a&gt; have this problem.  People have also &lt;a href=&quot;http://connect.microsoft.com/VisualStudio/feedback/details/486244/debugging-information-for-exe-cannot-be-found-or-does-not-match&quot;&gt;reported this problem on VS2008&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Oddly enough, no one else on my team has been complaining about this problem.  When I start VS2005, I can generally run the client in debug once or twice and then I perpetually get this error.&lt;br /&gt;&lt;br /&gt;Sometimes, exiting VS2005 and killing mspdbsrv.exe followed by a restart of VS2005 usually allows me to run a couple more times.  However, with our huge solution (112 projects), this takes a few minutes or so.  Definitely not conducive to small changes.&lt;br /&gt;&lt;br /&gt;Some of the links above have responses from MS folks who state that they aren&#39;t able to reproduce the problem, but there is most definitely a problem.&lt;br /&gt;&lt;br /&gt;I&#39;ve only experienced this problem on our internal AdminClient, which, unfortunately, is the project that I&#39;m most often trying to run.  Other projects in the solution file seem to work just fine.  I&#39;ve tried several suggestions from the above links, including others like replacing dbghelp.dll with the latest version, altering the symbol search paths, trying to manually load symbols, and so on.&lt;br /&gt;&lt;br /&gt;As a last-ditch effort, some of the links above suggest re-installing VS2005.  I&#39;ll give that a shot.&lt;br /&gt;&lt;br /&gt;If that doesn&#39;t work, I may have to become &lt;a href=&quot;http://www.arrogantworms.com/music/the-last-saskatchewan-pirate-6/&quot;&gt;a Pirate&lt;/a&gt; or something.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Update: &lt;/b&gt;(3/23/2011): I believe I have found a work-around by using the Remote Debugging Monitor.  More info in &lt;a href=&quot;http://kriegshauser.blogspot.com/2011/03/debugging-information-cannot-be-found.html&quot;&gt;this post&lt;/a&gt;.&lt;/div&gt;</description><link>http://blog.joshuakriegshauser.com/2011/02/current-bane-of-my-existence.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLWfllf7UidpRPeXBywbN7Je3FCgdvCgQeMZ1odAJkWpR_sxcys_zDHtSof2nWZc6G7AyxxGUDE698BUwoySV2csQB2OX52kNCvx9tNx1rXyeXWMK8VptqJ0Bb4fEp1J_blKChZWnWA/s72-c/NoDebuggingInfo.JPG" height="72" width="72"/><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4921926763862584482</guid><pubDate>Wed, 15 Sep 2010 14:14:00 +0000</pubDate><atom:updated>2010-09-15T07:17:52.491-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cwa</category><title>Ladies and Gentlemen, we are LIVE!</title><description>&lt;a href=&quot;http://www.clonewarsadventures.com/&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHOCgHG1Jm3RvkBrcRr_bTR3OnXdyNlEtCtAJljqb-0V_pUAcv1wpNsn1pB1ekYjlQhM3F8bukM4s5jDFFIk-f469OUFnxNq6ietX5zpzgkpShrLmY5FwMkFnMYFwy833iCzKU5TJHg/s320/CWA.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(Read the title in your best &lt;a href=&quot;http://en.wikipedia.org/wiki/Bruce_Buffer&quot;&gt;Bruce Buffer&lt;/a&gt; voice).&lt;br /&gt;&lt;br /&gt;My new game, &lt;a href=&quot;http://www.clonewarsadventures.com/&quot;&gt;Clone Wars Adventures&lt;/a&gt;, is now LIVE and out of Beta!  It&#39;s free to play and easy to download so head on over to the website and check it out!</description><link>http://blog.joshuakriegshauser.com/2010/09/ladies-and-gentlemen-we-are-live.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHOCgHG1Jm3RvkBrcRr_bTR3OnXdyNlEtCtAJljqb-0V_pUAcv1wpNsn1pB1ekYjlQhM3F8bukM4s5jDFFIk-f469OUFnxNq6ietX5zpzgkpShrLmY5FwMkFnMYFwy833iCzKU5TJHg/s72-c/CWA.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3550022708260187651</guid><pubDate>Sat, 28 Aug 2010 05:01:00 +0000</pubDate><atom:updated>2010-08-27T22:04:48.562-07:00</atom:updated><title>Open Beta!</title><description>&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHOCgHG1Jm3RvkBrcRr_bTR3OnXdyNlEtCtAJljqb-0V_pUAcv1wpNsn1pB1ekYjlQhM3F8bukM4s5jDFFIk-f469OUFnxNq6ietX5zpzgkpShrLmY5FwMkFnMYFwy833iCzKU5TJHg/s320/CWA.png&quot; /&gt;&lt;br /&gt;&lt;br /&gt;My new game, &lt;a href=&quot;http://www.clonewarsadventures.com/&quot;&gt;Clone Wars Adventures&lt;/a&gt;, is now in Open Beta!  Head on over, create an account and let us know what you think on the &lt;a href=&quot;http://forums.station.sony.com/cw/forums/list.m&quot;&gt;forums&lt;/a&gt; (must sign in with your Station name)!</description><link>http://blog.joshuakriegshauser.com/2010/08/open-beta.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHOCgHG1Jm3RvkBrcRr_bTR3OnXdyNlEtCtAJljqb-0V_pUAcv1wpNsn1pB1ekYjlQhM3F8bukM4s5jDFFIk-f469OUFnxNq6ietX5zpzgkpShrLmY5FwMkFnMYFwy833iCzKU5TJHg/s72-c/CWA.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1169485340356738960</guid><pubDate>Tue, 01 Jun 2010 16:48:00 +0000</pubDate><atom:updated>2010-06-01T13:47:37.241-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cwa</category><category domain="http://www.blogger.com/atom/ns#">soe</category><category domain="http://www.blogger.com/atom/ns#">star wars</category><title>Finally!</title><description>We just announced the new game that I&#39;m working on.  Finally I can talk about it!&lt;br /&gt;&lt;br /&gt;So, without further ado:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://www.clonewarsadventures.com/&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 320px; height: 208px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHOCgHG1Jm3RvkBrcRr_bTR3OnXdyNlEtCtAJljqb-0V_pUAcv1wpNsn1pB1ekYjlQhM3F8bukM4s5jDFFIk-f469OUFnxNq6ietX5zpzgkpShrLmY5FwMkFnMYFwy833iCzKU5TJHg/s320/CWA.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5477849937330418802&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is a new online free-to-play PC game targeted to kids (and adults) due out this fall.  It is built upon the technology first developed for &lt;a href=&quot;http://www.freerealms.com/&quot;&gt;Free Realms&lt;/a&gt;.  It will be largely mini-game based allowing things from Lightsaber Dueling to Speederbike Racing, the obligatory Starfighter missions and even a 3D tower defense game called Republic Defender.  However, there is still a large social aspect with friends, housing, leaderboards and even a kid-friendly &lt;a href=&quot;http://www.facebook.com/&quot;&gt;Facebook&lt;/a&gt;-inspired profile page.&lt;br /&gt;&lt;br /&gt;On a personal note, this is a great project to be on.  I am a huge Star Wars geek (over 15,000 LEGO bricks in my office representing different &lt;a href=&quot;http://www.youtube.com/watch?v=SqBF9mzSRss&quot;&gt;Star&lt;/a&gt; &lt;a href=&quot;http://www.youtube.com/watch?v=3l8Kdd99fNg&quot;&gt;Wars&lt;/a&gt; &lt;a href=&quot;http://www.youtube.com/watch?v=BvGXTAJqTWA&quot;&gt;ships&lt;/a&gt;) and my son loves Star Wars and the Clone Wars (and, incidentally, this game too!).&lt;br /&gt;&lt;br /&gt;I hope you enjoy the teaser site and video.  I look forward to actually being able to show in-game demos (*ahem* &lt;a href=&quot;http://www.e3expo.com/&quot;&gt;E3&lt;/a&gt; *cough*)!&lt;br /&gt;&lt;br /&gt;Update 6/1/2010 1:47PM PST:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://kotaku.com/5552522/free-realms-meets-star-wars-in-clones-wars-adventures&quot;&gt;Kotaku Article&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.starwars.com/games/clonewars_adventures_announced/index.html&quot;&gt;Starwars.com article&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://bit.ly/dlCIsp&quot;&gt;Gametrailers interview with Lead Designer Matt Higby&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://pc.ign.com/articles/109/1093853p1.html&quot;&gt;Press Release&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>http://blog.joshuakriegshauser.com/2010/06/finally.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHOCgHG1Jm3RvkBrcRr_bTR3OnXdyNlEtCtAJljqb-0V_pUAcv1wpNsn1pB1ekYjlQhM3F8bukM4s5jDFFIk-f469OUFnxNq6ietX5zpzgkpShrLmY5FwMkFnMYFwy833iCzKU5TJHg/s72-c/CWA.png" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-920465334908666890</guid><pubDate>Thu, 15 Apr 2010 07:51:00 +0000</pubDate><atom:updated>2010-04-15T01:31:42.755-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">repair</category><title>/resurrect iPhone</title><description>I broke my iPhone 3G.  Again.&lt;br /&gt;&lt;br /&gt;A few months ago I dropped it in the supermarket.  It landed flat on the screen and when I picked it up I was horrified to discover a spiderweb-shaped crack in the screen.  Fortunately, everything still worked.  For $70 I had it repaired at &lt;a href=&quot;http://www.yelp.com/biz/volt-mobile-and-tech-san-diego&quot;&gt;Volt Mobile&lt;/a&gt;.  They did pretty good work, but the touchscreen was intermittent (I&#39;d have to flex the phone to make it work again).&lt;br /&gt;&lt;br /&gt;Last week, I dropped it again.  This time the touchscreen was completely broken--I couldn&#39;t even turn the phone off or enter my passcode to do a backup.&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEd2k7QG1umBNyYx2namJK-aZgGmQImgyirW7TKg7xeAOjfCHEpAyJLYXo0uTDZXq1ei6YdCnhLjn-evJNWuPFMmfvAKS9FuwKH3hyphenhyphen-r7rR6gCAL8MaDfWV79Stc_31EuQAFO2ESgsWw/s1600/DSC01977.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEd2k7QG1umBNyYx2namJK-aZgGmQImgyirW7TKg7xeAOjfCHEpAyJLYXo0uTDZXq1ei6YdCnhLjn-evJNWuPFMmfvAKS9FuwKH3hyphenhyphen-r7rR6gCAL8MaDfWV79Stc_31EuQAFO2ESgsWw/s320/DSC01977.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460269986775898178&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This time, I figured I&#39;d try my own repair.  Hey, it&#39;s worked &lt;a href=&quot;http://kriegshauser.blogspot.com/2007/06/video-card-woes.html&quot;&gt;in&lt;/a&gt; &lt;a href=&quot;http://kriegshauser.blogspot.com/2007/07/resurrect-psp.html&quot;&gt;the&lt;/a&gt; &lt;a href=&quot;http://kriegshauser.blogspot.com/2008/11/resurrect-xbox-360_30.html&quot;&gt;past&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A quick look online shows several places that sell digitizers (touchpads), LCD screens, batteries, you name it.  I ended up going with a &lt;a href=&quot;http://www.amazon.com/gp/product/B002A9PVAI&quot;&gt;digitizer&lt;/a&gt; sold from &lt;a href=&quot;http://www.amazon.com/gp/help/seller/at-a-glance.html?ie=UTF8&amp;amp;seller=A1TB4CRIUIFO5O&quot;&gt;AccessoryOne&lt;/a&gt; (which purports to be OEM) for less than $20 shipped and taxed.  There is also a &lt;a href=&quot;http://www.youtube.com/watch?v=8fF0DF-mbn4&quot;&gt;three&lt;/a&gt;-&lt;a href=&quot;http://www.youtube.com/watch?v=p3xn7whpZAA&quot;&gt;part&lt;/a&gt; &lt;a href=&quot;http://www.youtube.com/watch?v=w5LSqMoD4WE&quot;&gt;video&lt;/a&gt; series on cracking open the iPhone and swapping out the digitizer.  The videos are made by &lt;a href=&quot;http://www.directfix.com/&quot;&gt;http://www.directfix.com/&lt;/a&gt; who also have parts but tend to charge a bit more.&lt;br /&gt;&lt;br /&gt;This blog basically chronicles my repair.  If you&#39;re doing this yourself, watch the videos; they&#39;re much more informative.  Also, there is no guarantee that this will work, and it will void your warranty (mine has already expired).  I just figured that I&#39;ll be upgrading to the 4th-generation iPhone when it comes out anyways.&lt;br /&gt;&lt;br /&gt;The first step was pulling out the screen assembly.  Two screws from the base (near the speakers) are all that is holding the screen assembly on.  Unlike the videos, I used a suction cup on the screen near the home button to pull the screen out.  I believe this works better and doesn&#39;t molest the rubber seal around the screen assembly.  This picture shows the screen assembly removed from the base:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFjGPI46XdulQViBTtwTgR_WM2mAEDsHwA0BXE-JCqY7D6ajsPBHoNYEZ2oWFRGirhfKXSO6uM-fQxjaMMHnSyFi1fEQoqVNJ9EU2-qLlIMk5Qw0FYdrFDfHrIMmZZvow_FgsDJMDI6A/s1600/DSC01978.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFjGPI46XdulQViBTtwTgR_WM2mAEDsHwA0BXE-JCqY7D6ajsPBHoNYEZ2oWFRGirhfKXSO6uM-fQxjaMMHnSyFi1fEQoqVNJ9EU2-qLlIMk5Qw0FYdrFDfHrIMmZZvow_FgsDJMDI6A/s320/DSC01978.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460273048683871474&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This picture shows the new digitizer/glass on the left top, the removed LCD screen on the left bottom and the old frame/digitizer assembly on the right bottom:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-E37Kqsora9RmZKdHkbNLvuF9PNFoUU-ZTpP8Fxu05baGnxhjsPpdcFrdQcdfo0f6eE9Y3H_tm8pVcKCAiylWeVj54QszzA6e1rr-zoTXgCevlmPgImDu7cs-YsTvLCFCMuJ8aIPuhw/s1600/DSC01979.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-E37Kqsora9RmZKdHkbNLvuF9PNFoUU-ZTpP8Fxu05baGnxhjsPpdcFrdQcdfo0f6eE9Y3H_tm8pVcKCAiylWeVj54QszzA6e1rr-zoTXgCevlmPgImDu7cs-YsTvLCFCMuJ8aIPuhw/s320/DSC01979.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460273525001322482&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Removing the LCD screen is tricky and must be done carefully.  I&#39;ve read posts from people who crack their LCD screens at this step and it&#39;s a much more &lt;a href=&quot;http://www.amazon.com/Apple-iPhone-Display-Assembly-Replacement/dp/B002VW3YLC/ref=sr_1_2?ie=UTF8&amp;amp;s=wireless&amp;amp;qid=1271319081&amp;amp;sr=1-2&quot;&gt;expensive component&lt;/a&gt;.  I was very gentle.&lt;br /&gt;&lt;br /&gt;The digitizer that I ordered was just the glass/digitizer combo; it didn&#39;t include the screen frame or the components that are mounted to it (speaker, skin sensor, home button, etc).  Therefore, the frame must be removed from the original glass/digitizer and attached to the new one.  This was probably the most tricky part.  It involves using a hair dryer to weaken the glue and pry the frame off.  Then new two-sided tape must be applied to the frame and attached to the new glass/digitizer.&lt;br /&gt;&lt;br /&gt;This picture shows the removed frame (bottom center) and the old cracked digitizer on the bottom right:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR-sfwo6YMoXry2-XwK5qiZURfN_5RaFx5bj4QXaxXqqj_HIoMVhCF3NJ1kIo5cz2S_0UN9FJJcp9pXxIxnnk8iIzu-6lx8Zi1X39uXjyb1LCm5YYwxWovC_leHPdc1xuLNQf58Jsd0g/s1600/DSC01981.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiR-sfwo6YMoXry2-XwK5qiZURfN_5RaFx5bj4QXaxXqqj_HIoMVhCF3NJ1kIo5cz2S_0UN9FJJcp9pXxIxnnk8iIzu-6lx8Zi1X39uXjyb1LCm5YYwxWovC_leHPdc1xuLNQf58Jsd0g/s320/DSC01981.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460275475513881170&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After a few minutes of work, the frame was attached to the new glass/digitizer:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDxb9spXvbEOImb8H1yCS4-m5ljfdI6mSxQqJAup2uKBDVCWoJcg3PNHfxw1h_0VLefAesR9mryK0r1MgPcNMpiiLzTugUmdjkAki4qGlUqSS4_cwla_QDp2J9GrWEhgfZOThIMYC7uw/s1600/DSC01982.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDxb9spXvbEOImb8H1yCS4-m5ljfdI6mSxQqJAup2uKBDVCWoJcg3PNHfxw1h_0VLefAesR9mryK0r1MgPcNMpiiLzTugUmdjkAki4qGlUqSS4_cwla_QDp2J9GrWEhgfZOThIMYC7uw/s320/DSC01982.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460276244575680674&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The new glass had an appropriate amount of protective film, so it was fairly easy to minimize dust getting in between the screen and the glass.  The new screen assembly with LCD installed is now shown on the bottom:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoBKrWZr5vDnT0N4yQCxg9aCGAbXHIwnxWEPpGZjYDdLfoTXuWTmaz1PeIqHiaGb195JJIpecWsI-oE4p7qol70IHI3GgQMvdj5hx_jf0vgFmlKfjuEEd_ddmHyoha9OxoFlM8waIPJQ/s1600/DSC01983.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 240px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoBKrWZr5vDnT0N4yQCxg9aCGAbXHIwnxWEPpGZjYDdLfoTXuWTmaz1PeIqHiaGb195JJIpecWsI-oE4p7qol70IHI3GgQMvdj5hx_jf0vgFmlKfjuEEd_ddmHyoha9OxoFlM8waIPJQ/s320/DSC01983.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460276946631786562&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After getting the screen cables connected I powered on the phone and was able to enter my passcode to unlock the phone (note that the protective film is still on the screen, hence the sticker in the middle of the screen):&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM5RNxBrmjB7-yjqTSsRpWcNWLjvC9zZFE4sKJW7LpUa8X8o6p3jsFNKUzjxZ1zPInG3UQyLOs6fXJ6jA5Ip6Q2aZzblaqYp4x1cByefWy4hRRjwoO3Lrp1rbj9Ufx3d8UWAXv22_lHw/s1600/DSC01984.JPG&quot;&gt;&lt;img style=&quot;cursor: pointer; width: 240px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM5RNxBrmjB7-yjqTSsRpWcNWLjvC9zZFE4sKJW7LpUa8X8o6p3jsFNKUzjxZ1zPInG3UQyLOs6fXJ6jA5Ip6Q2aZzblaqYp4x1cByefWy4hRRjwoO3Lrp1rbj9Ufx3d8UWAXv22_lHw/s320/DSC01984.JPG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5460277363091236466&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Yay!  All-in-all, a very successful repair for under $20 and about an hour.&lt;br /&gt;&lt;br /&gt;Now... to find a better case...</description><link>http://blog.joshuakriegshauser.com/2010/04/resurrect-iphone.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEd2k7QG1umBNyYx2namJK-aZgGmQImgyirW7TKg7xeAOjfCHEpAyJLYXo0uTDZXq1ei6YdCnhLjn-evJNWuPFMmfvAKS9FuwKH3hyphenhyphen-r7rR6gCAL8MaDfWV79Stc_31EuQAFO2ESgsWw/s72-c/DSC01977.JPG" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-8366223773760887500</guid><pubDate>Tue, 16 Mar 2010 06:08:00 +0000</pubDate><atom:updated>2010-03-15T23:35:33.254-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">eq2</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Be Careful with Default Arguments</title><description>&lt;pre&gt;CRC16::computeMemory( const char* data, unsigned length, uns16 crc = 0 );&lt;br /&gt;CRC16::computeString( const char* data, uns16 crc = 0 );&lt;/pre&gt;This little gem caused quite a headache.  Allow me to explain.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.everquest2.com/&quot;&gt;EQII&lt;/a&gt;&#39;s &lt;a href=&quot;http://kriegshauser.blogspot.com/2010/02/evolution-of-streaming-client.html&quot;&gt;streaming client&lt;/a&gt; uses 16-bit &lt;a href=&quot;http://en.wikipedia.org/wiki/Cyclic_redundancy_check&quot;&gt;CRCs&lt;/a&gt; to tell if an asset has changed.  All 500,000 individual assets have a 16-bit CRC calculated and stored in the master asset list (manifest).  When an asset is downloaded, it is cached on disk and stored with the 16-bit CRC.  To save time, the client doesn&#39;t calculate the CRC on each asset that it downloads, it just uses the CRC stored in the manifest.&lt;br /&gt;&lt;br /&gt;Every time the client runs, we download the manifest and check the CRCs in the manifest against the stored CRCs for our cached assets to see if any changed.  If an asset has changed (the CRC is different), we delete the cached copy and request the replacement, even if it&#39;s not immediately needed.&lt;br /&gt;&lt;br /&gt;When the streaming client launched, everything worked as planned.  It worked great! Little did we know that a particularly evil bug was lurking.&lt;br /&gt;&lt;br /&gt;I discovered a problem when I wrote a utility to convert old PAK files (the game data shipped on the DVD) to cached streaming assets.  NOTHING matched the CRCs in the manifest.  Every asset was wrong.  I looked over the code several times and everything looked fine.  This bug didn&#39;t make sense.&lt;br /&gt;&lt;br /&gt;And then it hit me.&lt;br /&gt;&lt;br /&gt;The code that was building the manifests was doing this:&lt;br /&gt;&lt;pre&gt;uns16 crc = CRC16::computeString( data, dataLength );&lt;/pre&gt;Talk about &lt;a href=&quot;http://images.google.com/images?q=facepalm&quot;&gt;/facepalm&lt;/a&gt;.  The function I &lt;span style=&quot;font-style: italic;&quot;&gt;meant &lt;/span&gt;to call was &lt;code&gt;CRC16::computeMemory()&lt;/code&gt;.  The function I &lt;span style=&quot;font-style: italic;&quot;&gt;actually &lt;/span&gt;called treated the input like a &lt;a href=&quot;http://en.wikipedia.org/wiki/C_string&quot;&gt;null-terminated string&lt;/a&gt;.  This means that the CRC was only calculated up to the first &lt;code&gt;NUL&lt;/code&gt; character and the &lt;code&gt;dataLength&lt;/code&gt; parameter was actually being treated as a starting CRC value.  This was a bug that &lt;span style=&quot;font-style: italic;&quot;&gt;had &lt;/span&gt;to get fixed.  Someday, perhaps many years from now, this would be a huge bug that would waste a lot of someone&#39;s time to hunt down.&lt;br /&gt;&lt;br /&gt;Oh, but the fun doesn&#39;t end there.  I couldn&#39;t just change the function to fix the bug.  Doing that would mean that every streaming client user would have to re-download &lt;span style=&quot;font-style: italic;&quot;&gt;everything&lt;/span&gt; that they had already downloaded.  Every CRC would change and the naïve client would happily delete everything and start over.  To fix this properly would take a highly-synchronized effort to fix and push the manifests while deploying a one-time-only tool that would re-calculate the CRC for all assets that people had already downloaded.&lt;br /&gt;&lt;br /&gt;The moral of the story: be careful with default arguments.  They can really hurt if misused.</description><link>http://blog.joshuakriegshauser.com/2010/03/be-careful-with-default-arguments.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3430846014252754770</guid><pubDate>Wed, 10 Feb 2010 03:35:00 +0000</pubDate><atom:updated>2010-02-09T23:35:02.508-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">eq2</category><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">technology</category><title>Evolution of a Streaming Client</title><description>It&#39;s funny how many things in the game industry start out as &quot;I wish...&quot;&lt;br /&gt;... I wish we had Guild Halls.&lt;br /&gt;... I wish we had Shader 3.0 support.&lt;br /&gt;... I wish we had Battlegrounds.&lt;br /&gt;&lt;br /&gt;... I wish our game was easier to download.&lt;br /&gt;&lt;br /&gt;It&#39;s equally funny how many things are started by people in their own time just trying to make the game better.  That&#39;s how &lt;a href=&quot;http://www.everquest2.com/&quot;&gt;EverQuest II&lt;/a&gt;&#39;s &lt;a href=&quot;http://everquest2.com/get_the_game/free_trial&quot;&gt;streaming client&lt;/a&gt; started out.&lt;br /&gt;&lt;br /&gt;Taking an existing game (with 12GB of client assets no less) and streaming it is no simple task.  I started off with a &quot;proof of concept&quot; just to prove that it could be done with EverQuest II.  As I got into it, the concept became a full fledged project.  It wasn&#39;t officially on the schedule, so it was really a labor of love on my part.  After a few weeks of silently working on it, I called the producer into my office and said, &quot;Hey, check this out.&quot;  Needless to say he was pretty surprised.&lt;br /&gt;&lt;br /&gt;There are three major conversion steps for a streaming system.&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;br /&gt;Serving the assets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;EverQuest II has roughly 500,000 client-side asset files: meshes, textures, collision meshes, shaders, data files, sounds, music, you name it.  Have you ever tried putting half-a-million tiny files in a directory?  Take my word for it: Don&#39;t.&lt;br /&gt;&lt;br /&gt;My first inclination was to build a custom server.  The server would run off of the PAK files that we already ship with the DVD-based game client.  I had grandiose plans about how to track files that clients were downloading and automatically send assets to clients that they didn&#39;t know they needed.&lt;br /&gt;&lt;br /&gt;But alas, it was not to be.  A custom server means that every client would have to be talking to our server.  We would have to think about where to place the server geographically, handling varying load characteristics, availability, bandwidth, etc.  These were all questions that had already been answered; we didn&#39;t need to ask them again and try to come up with our own answers.&lt;br /&gt;&lt;br /&gt;What else is great at serving files to a large number of clients all over the world?  &lt;a href=&quot;http://en.wikipedia.org/wiki/Web_server&quot;&gt;Web servers&lt;/a&gt;!  Specifically, &lt;a href=&quot;http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol&quot;&gt;HTTP&lt;/a&gt; servers.  We already used a &lt;a href=&quot;http://en.wikipedia.org/wiki/Content_delivery_network&quot;&gt;CDN&lt;/a&gt; for patching purposes--we just needed to serve all the game assets individually and on-demand now.&lt;br /&gt;&lt;br /&gt;This caused another wrinkle.  The client needs to know a list of all the assets that are available and whether the assets that it has previously downloaded are out of date.  We call this the &quot;manifest.&quot;  This manifest must be fully up-to-date before the client tries to load ANY assets.  My custom server knew how to negotiate a manifest with the client in a fairly bandwidth-friendly way because it was smart.  CDNs are less smart--they just serve files.  EQII&#39;s manifest is about 6MB, which you definitely don&#39;t want to download every time you run the game.  The solution I developed involves parts of the manifest available as separate files and an overarching CRC file that is requested first.  The CRC file is always requested, but it&#39;s only about 8KB.  Based on comparisons with the CRC file, the client reconstructs the full manifest by grabbing parts that it needs.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;br /&gt;Requesting the assets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Compared to everything else, serving the assets is the &quot;easy&quot; part.  Requesting the assets is far more difficult.  You&#39;re essentially replacing file system access with a network connection.  That sounds a lot easier than it is.  File system access is inherently goverened by the Operating System and allows any thread to open nearly any file and read data from it.  A network connection is a single pipe (or in our case, a collection of pipes) that must well-defined and tightly-controlled access.  Any thread that could just expect to read from a file at any point must now be synchronized with other threads requesting assets from a network resource.&lt;br /&gt;&lt;br /&gt;Another major difference is that file system access is &lt;span style=&quot;font-style: italic;&quot;&gt;synchronous &lt;/span&gt;from an application&#39;s perspective.  This means that while waiting for the Operating System to read data from a file, the thread goes to sleep and allows the system to do other things.  Generally this happens so quickly that you barely notice, but network connections aren&#39;t nearly as fast as your local hard disk.  For this reason, we want most of our asset requests to be &lt;span style=&quot;font-style: italic;&quot;&gt;asynchronous&lt;/span&gt;: we send the asset request and go about doing other things until it finishes at some later time.&lt;br /&gt;&lt;br /&gt;Unfortunately, it&#39;s much easier to do synchronous reads than asynchronous.  The EverQuest II client had many synchronous reads that you didn&#39;t even notice because the file system is fast enough.  If they weren&#39;t made asynchronous, a streaming client would appear to &#39;lock up&#39; while waiting for an asset to be fetched.  Obviously, this is undesirable, and nearly unavoidable in some cases.&lt;br /&gt;&lt;br /&gt;Furthermore, network connections in games are usually given time by the main thread to do their work (colloquially referred to as &quot;pumping&quot;).  That won&#39;t work in this system.  What if the main thread needs to synchronously load an asset (which still happens occasionally, especially on client startup)?  It would be waiting for an asset to finish loading and wouldn&#39;t be able to update the network connection that it is effectively waiting on.&lt;br /&gt;&lt;br /&gt;Clearly, a system is needed that can pump itself.  Any thread can request an asset synchronously or asynchronously and the network connection continues updating as long as the client is running.  The system should be able to determine if a request for an asset has already been sent and we don&#39;t need to waste bandwidth by requesting it again.  The system should be able to recognize and quickly send higher priority requests.  And, oh yes, let&#39;s not forget about failure cases.  This piece of technology is the very heart of the streaming client.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Storing the assets&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;br /&gt;&lt;/span&gt;Obviously&lt;span style=&quot;font-weight: bold;&quot;&gt;, &lt;/span&gt;once an asset has been downloaded, we don&#39;t want to waste bandwidth downloading that asset again.  It might take minutes to enter a zone for the first time, but we don&#39;t want to take that long every time we enter that zone.  Therefore, that asset must be stored locally.&lt;br /&gt;&lt;br /&gt;A possibility is to store each asset as its own file, but this fails in practice.  Operating Systems are not optimized for hundreds of thousands of tiny files.  No, these files must be stored in a larger file, packed together and easily accessible.&lt;br /&gt;&lt;br /&gt;EQII already has a packed file format.  Unfortunately, the way it&#39;s set up does not lend itself to modification.  When EQII&#39;s packed files are written, they&#39;re never intended to change.  With new assets being downloaded constantly, these files will be changing, and often.&lt;br /&gt;&lt;br /&gt;My solution was to develop a new type of asset database specifically suited to our needs.  These database files can store a large number of tiny assets, rapidly add and remove assets and quickly retrieve individual assets.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Other Considerations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The most difficult part of building a streaming system for an existing client has been trying to change synchronous asset requests into asynchronous.  Consider the following simple example:&lt;br /&gt;&lt;pre&gt;Animation* pAnim = pAssetSystem-&gt;LoadAnimation( &quot;animation/player_anim1&quot; );&lt;br /&gt;if ( pAnim )&lt;br /&gt;{&lt;br /&gt;    // Do something with loaded asset&lt;br /&gt;}&lt;/pre&gt;The above example would need to fetch &lt;code&gt;player_anim1&lt;/code&gt; synchronously.  Changing this to be asynchronous might look like the following example:&lt;br /&gt;&lt;pre&gt;Asset&amp;lt;Animation&amp;gt; anim( &amp;amp;myAnimLoadHandler );&lt;br /&gt;pAssetSystem-&gt;StartLoad( &amp;amp;anim, &quot;animation/player_anim1&quot; );&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;void AnimLoadHandler::OnLoaded( Asset&amp;lt;Animation&amp;gt;&amp;amp; a )&lt;br /&gt;{&lt;br /&gt;    // Do something with loaded asset&lt;br /&gt;}&lt;/pre&gt;There&#39;s much complexity missing from the second example, but the point should be clear: making something asynchronous is much more difficult than making something synchronous.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Working on the streaming client was one of the most fun projects that I&#39;ve ever worked on in a technical sense.  It was challenging, but the results are a huge payoff.</description><link>http://blog.joshuakriegshauser.com/2010/02/evolution-of-streaming-client.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-6617820520036942060</guid><pubDate>Wed, 11 Nov 2009 19:54:00 +0000</pubDate><atom:updated>2009-11-11T13:14:49.161-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">commentary</category><category domain="http://www.blogger.com/atom/ns#">personal</category><title>Dr. Laura: Dr Jekyll and Mr. Video Gamer</title><description>&lt;a href=&quot;http://www.drlaura.com/&quot;&gt;Dr. Laura&lt;/a&gt; has a video blog about a man who plays video games for his down time:&lt;br /&gt;&lt;object width=&quot;480&quot; height=&quot;295&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/OoljFL1C0DI&amp;hl=en&amp;fs=1&amp;rel=0&amp;color1=0x006699&amp;color2=0x54abd6&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.youtube.com/v/OoljFL1C0DI&amp;hl=en&amp;fs=1&amp;rel=0&amp;color1=0x006699&amp;color2=0x54abd6&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;480&quot; height=&quot;295&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Now, I actually enjoy reading most Dr. Laura articles.  Her common-sense approach to most family psychology issues provides both entertainment and self-reflection.&lt;br /&gt;&lt;br /&gt;However, sometimes she&#39;s just a little too old-fashioned for me.   She seems to come off in this video as entirely anti-video-game.  I&#39;ll grant that the guy mentioned in the video is acting like a complete idiot, but not all video games are bad.  Consoles like the &lt;a href=&quot;http://wii.com/&quot;&gt;Wii&lt;/a&gt; consistently encourage family gameplay.&lt;br /&gt;&lt;br /&gt;Maybe it&#39;s just because I&#39;m in the video game industry, but if a guy wants to play video games for his downtime, that seems fine.  Of course, you have to look at your priorities.  Personally, family comes first.  Always.  I will usually sit down to play games after the rest of my family has turned in for the night.  If the rest of the family is napping, occasionally my son and I will enjoy &lt;a href=&quot;http://starwars.lego.com/en-us/VideoGame/Default.aspx&quot;&gt;LEGO Star Wars&lt;/a&gt; or some other innocuous game.&lt;br /&gt;&lt;br /&gt;Everything in moderation, but please don&#39;t demonize all video games because some idiot guy can&#39;t put down the controller and spend time with his family.</description><link>http://blog.joshuakriegshauser.com/2009/11/dr-laura-dr-jekyll-and-mr-video-gamer.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>2</thr:total></item></channel></rss>