Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

edith

edith@exclusivelyducks.com

http://github.com/dschleck/edith

A Dota 2 replay (.dem) parser that understands packet entities. Tested on OS X and Ubuntu.

Kills in SL2 Na`Vi v Mouz game 1

Quick start:

cd build
ccmake ../
make
./death_recording <a replay>

If you encounter errors compiling the protocol buffers, make sure the CMake variable PROTOBUF_INCLUDE_DIR points to the directory containing google/protobuf/descriptor.proto. The other PROTOBUF_ variables should be set as well.

Your C++ is terrible:

This is a reverse engineering project and I stopped caring the moment this worked, sorry. The great news is others have used this as a reference to write this better!

Overview:

Parsing packet entities requires several components of knowledge, luckily each replay contains everything needed to parse it.

The first thing to understand are the send properties (send props). The primary use for these is to describe a variable in a class. As a simple and imprecise example, suppose Dota had the following class they needed to synchronize:

class Ent {
float x;
int y;
};

To describe this class, they could have a send prop named x with type 1 (float) and a send prop named y with type 0 (int). These send props could be contained in a send table named Ent.

Beyond simply adding additional types (strings, int64s, arrays) send props can also point at other send tables whose props should also be included. The primary use of this is for inheritance, most send tables have a send prop named "baseclass" that points at the send table of the parent class.

The other big source of complexity comes from exclude props. Suppose Dota had some class that inherited from Ent, but no longer used Ent.y. To prevent wasting bandwidth, they could add an exclude prop that will prevent it from being transmitted. The name of this prop would be y and it would also have a field called datatable name set to Ent.

After we read in all of the send tables from the replay we have to flatten them. When a table is flattened, we recursively follow all the links (such as baseclass) to other send tables and copy all of their send props into the table being flattened.

Before we can parse the packet entities, there is one efficiency optimization. Suppose there's a class with two hundred integers and every instance sets those to zero. Rather than transferring two hundred zeroes every time a new instance of this class is created, it would be better to have a default instance of the class and then transfer only what is changed from the default when a new instance is created. The instancebaseline table accomplishes this, every class has an entry in the table with a default instance.

Finally we can parse our packet entities. When the server thinks an entity enters the client's potentially visible set (PVS) it tells the client to make a new entity. Making a new entity requires first parsing the entry in instancebaseline, and then updating the instance by parsing the data sent in the packet. Parsing both of these is exactly the same, first you read the list of properties that they contain and then you read each property.

Entities are updated using the same mechanics. First you read the list of properties in the update and then you read each property and put them in the entity.

Deleting an entity only requires transferring the ID of the entity.

Understanding the code:

examples/death_recording_visitor is an example of doing something useful with my code. This outputs a line whenever a hero dies. This was used to gather data for the tool that produced the image above.

src/entity describes an entity and stores its properties.

src/property handles the different types of send props and stores the correct data for each one.

src/state contains a bunch of data structures read from the replay and handles flattening send tables.

src/edith reads the replay, converts it into the internal representation used by the program, and runs the logic for everything but flattening send tables.

Limitations:

This seems to work on the replays that I have tried, however there are probably a million different bugs hiding. The read_float_coord_mp is almost certainly completely buggy.

I implemented just enough logic that all the replays I have parse successfully. In particular the following are not implemented:

  • There are a million variations of float serialization. I only implemented the ones required for the send props in my replays.
  • There are a bunch of string tables that I can't read. The ones I've seen appear to be related to precaching, but there may be more.

If you have a replay that works in the client but doesn't parse correctly, let me know!

About

No description, website, or topics provided.

Resources

Stars

37 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages