Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon
, '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

Fix ScriptHook_t initialization order - #320

Merged
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init
Feb 1, 2025
Merged

Fix ScriptHook_t initialization order#320
Blixibon merged 1 commit into
mapbase-source:developfrom
z33ky:vscript-hook-delayed-init

Conversation

@z33ky

@z33kyz33ky commented Sep 5, 2024

Copy link
Copy Markdown

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member.
Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand.
Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit.

On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work.

This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, with the proper value for pHelper, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization.

Fixes#244.


Does this PR close any issues?

PR Checklist

  • My PR follows all guidelines in the CONTRIBUTING.md file
  • My PR targets a develop branch OR targets another branch with a specific goal in mind

@samisalreadytaken

Copy link
Copy Markdown

This breaks instance helper fallback assignment in BEGIN_SCRIPTDESC_NAMED for some classes. In MSVC, CBaseAnimating (baseanimating.cpp) won't be assigned a helper.

Test ToString in game with

printl( Entities.CreateByClassname("prop_dynamic") )

It should print ([0] prop_dynamic), but it prints (CBaseAnimating : 0x00)

@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from 6a2a58c to e80efe0CompareNovember 20, 2024 21:58
@z33ky

z33ky commented Nov 20, 2024

Copy link
Copy Markdown
Author

Hm yeah, I mistook this loop to initialize pHelper completely, but it's actually done through DEFINE_SCRIPT_INSTANCE_HELPER(). It is thus part of the delayed initialization and the loop won't find the proper helper instance.

One solution, which breaks compatibility with upstream vscript though, would be to include the helper as argument to BEGIN_SCRIPTDESC_NAMED(), so it can be initialized eagerly. A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros, which are the actual ones that must be delayed. We can rely on the optimizer to eliminate the redundant checks for !init. For this to work though, DEFINE_SCRIPT_INSTANCE_HELPER()must be called before the script-hook definitions, lest it silently breaks the helper.
I've gone with the first one for now, since there are relatively few script definitions that define a helper.

When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively
invokes its parents initializers in order to obtain their pHelper
member.
Initialization is only done once, so repeated initialization is skipped.
Initialization includes assignment of a vector of ScriptHook_t's
(DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized
beforehand.
Both of these use (static) globals; Within a translation unit,
initialization order is defined to be the same as the order of
declaration. So within a translation unit we must define all
ScriptHook_t's before the ScriptClassDesc_t using them.
A problem occurs with the parent initialization though, since there is
no defined order between translation units, meaning initialization of a
ScriptClassDesc_t can happen before its ScriptHook_t's, despite being
the correct order within its translation unit.
On MSVC it seems this issue is benign. On GCC/Linux however the
initialization of a ScriptHook_t essentially cleared whatever happened
during the initialization of the ScriptClassDesc_t, meaning many hooks
simply didn't work.
This situation is remedied by delaying the initialization of the
ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of
it is invoked from its translation unit. This is accomplished simply by
adding a boolean parameter to the function (GetScriptDesc()) that is
true in the global constructor invocation, and false by default
(including when doing parent ScriptClassDesc_t initialization).
When false, a valid ScriptClassDesc_t pointer is still returned, which
is all that is needed for the initialization of the child
ScriptClassDesc_t. The value of the returned pointer is a fixed memory
location, and does not change due to the delayed initialization.
The script-helper must be initialized eagerly though, for the search of
a base-class helper. This also changes the SCRIPTDESC slightly to
accommodate the eager initialization of helper instance pointer.
Fixesentropy-zero#244.
@z33ky
z33kyforce-pushed the vscript-hook-delayed-init branch from e80efe0 to 6297d2bCompareNovember 20, 2024 22:02
@samisalreadytaken

Copy link
Copy Markdown

A compatible, but brittle, solution would be to move the early if (!init) return pDesc; to the script-hook macros

I don't think this would be bad. There could even be something like BEGIN_SCRIPTHOOK_DEFINITIONS to indicate the definition section, or just put a note saying hooks should be defined last. Changing script hooks might be preferable over diverging from upstream. I personally don't have a strong preference of one over the other though.

@BlixibonBlixibon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that compatibility with upstream should be prioritized, and the alternate solution would be easier to accept than the current one. However, I also think the current solution is acceptable due to the rarity of instance helpers, and the fact it would be more clear what needs to be changed in cases where it breaks an instance helper in a fork not covered by this PR (which I wouldn't expect to be a common occurrence).

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

@Blixibon
Blixibon merged commit 4c9d71f into mapbase-source:developFeb 1, 2025
@z33ky

z33ky commented Feb 8, 2025

Copy link
Copy Markdown
Author

I'm approving and merging this with the assumption that no further changes are going to be made regarding that subject (or regarding the fix as a whole), although another PR can be opened before the next update if needed.

Thanks. Apart from the possible change to the "upstream-compatible but brittle" DEFINE_SCRIPT_INSTANCE_HELPER() I have nothing further to add. Feel free to tag me if I can help, should something come up.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@z33ky@samisalreadytaken@Blixibon