Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional comments #3080

Merged
merged 2 commits into from
Oct 31, 2021
Merged

Conversation

MishimaHaruna
Copy link
Member

@MishimaHaruna MishimaHaruna commented Oct 30, 2021

Pull Request Prelude

Changes Proposed

Note: This is a feature that I initially developed for personal use (drafted in 2015, in use in a much less refined form since 2017), but since there seems to be some interest in having it as part of the Hercules core, I decided to clean up the code a bit so that it's more usable and release it as part of Hercules.

This adds support for conditional comments in scripts.

Conditional comments are similar to #ifdef directives in the C preprocessor, allowing to exclude parts of a script depending on whether a certain flag is enabled or not.

The feature is designed to offer backward compatibility to scripts that use it, letting older engines seamlessly ignore the conditional part as if it was a comment.

Syntax

A conditional comment begins with /*@CONDITION followed by whitespace or */ and ends with the first */ sequence encountered on a separate line, but the sequence //@*/ at the beginning of a line (optionally with indentation) is recommended as a terminator, since it provides compatibility with older versions of the script engine that don't support conditional comments.

The condition flag can be negated by prefixing it with an exclamation mark (!).

Condition flags consist of an uppercase letter followed by a sequence of uppercase letters, digits and underscores and are at least three characters long.

Note: conditional comments cannot be nested (in the same way how block comments cannot be nested).

Examples

The following block is only parsed if FLAG is defined. If not defined, or when loaded by an older engine, the block is treated as a block comment:

/*@FLAG
.@x = 1;
//@*/

The following block is only parsed if FLAG is not defined. If defined, or when loaded by an older engine, the block is treated as a block comment:

/*@!FLAG
.@x = 1;
//@*/

The following block is only parsed if FLAG is defined. If not defined, the block is treated as a block comment. When loaded by an older engine, the block is also parsed:

/*@FLAG*/
.@x = 1;
//@*/

The following block is only parsed if FLAG is not defined. If defined, the block is treated as a block comment. When loaded by an older engine, the block is also parsed.

/*@!FLAG*/
.@x = 1;
//@*/

Condition flags

The Hercules core provides the following conditional comment flags (plugins may provide more):

Flag Decription
TRUE Always defined
HERCULES Always defined
FALSE Never defined
RENEWAL Only defined when compiled in renewal mode
PRERENEWAL Only defined when compiled in pre-renewal mode
LOADGMSCRIPTS Only defined when script_configuration.load_gm_scripts is true

Applications

A first application of the flags is included in this pull request: conditionally loading the GM management scripts (that may be considered by some as a security hazard) based on a server setting.

When the script_configuration.load_gm_scripts flag in the script configuration is set to false, the LOADGMSCRIPTS conditional feature is not defined in the script engine.

Compliant scripts put their GM management NPCs in a conditional block controlled by that flag (all the built-in scripts have been updated to support this feature).

Plugins

Plugins can define conditional flags at runtime by calling script->declare_conditional_feature("FLAG_NAME"), allowing scripts to behave differently when a plugin is loaded.

One such example is the naviluagenerator plugin in the StaffPlugins repository. The plugin defines the NAVILUAGENERATOR feature and compliant scripts can define conditional code to generate better navi lua scripts as follows:

Example exposing a warper NPC as a normal warp to the navi lua:

/* Overrides for navi link generation */
/*@NAVILUAGENERATOR
// The following line is only defined when the navi lua generator plugin is loaded. It defines a fake warp
// where the Lighthalzen guard stands, to let the navi generator know there's a route through the guard.
lhz_in01,35,226,0	warp	Rekenber Guard#li01-w	2,2,lhz_in01,37,225
//@*/
lhz_in01,35,226,5	script	Rekenber Guard#li01	4_M_LGTGUARD2,{
	if (isequipped(2241) && isequipped(2243)) {
		mes "[Rekenber Guard]";
		mes "^3355FF(Whoa, it's a member";
		mes "of the staff!)^000000 Good day!";
		close2;
		warp "lhz_in01",37,225;
		end;
	}
	// rest of the script omitted for example purposes
}

Example hiding a super secret quest NPC from the navi system:

/* Block disabled for navi link generation */
/*@!NAVILUAGENERATOR*/
morocc,140,155,3	script	Eddie#secret	4_M_ROGUE,{
	mes "[Eddie]";
	mes "Hi! I'm a super secret quest NPC";
}
//@*/

Example exposing a scripted mob spawn as a normal monster to the navi system:

/* Block disabled for navi link generation */
/*@!NAVILUAGENERATOR*/
lhz_dun03,2,2,0	script	summon_boss_lt	FAKE_NPC,{
OnInit:
	initnpctimer;
	end;

OnTimer6000000:
	if (rand(1,6) == 1) {
		donpcevent "summon_boss_lt::OnSummon";
		stopnpctimer;
	}
	end;
	// Rest of the script omitted for example purposes

OnMyMVPDead:
	killmonster "lhz_dun03","summon_boss_lt::OnMVP";
	initnpctimer;
	end;

//Required to keep from erroring
OnMVP:
	end;
}
//@*/

/* Overrides for navi link generation */
/*@NAVILUAGENERATOR
lhz_dun03,0,0,0,0	monster	Lord Knight Seyren	1640,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	Assassin Cross Eremes	1641,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	MasterSmith Howard	1642,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	High Priest Margaretha	1643,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	Sniper Cecil	1644,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	High Wizard Kathryne	1645,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	Lord Knight Seyren	1646,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	Assassin Cross Eremes	1647,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	MasterSmith Howard	1648,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	High Priest Margaretha	1649,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	Sniper Cecil	1650,1,6000000,1800000,1
lhz_dun03,0,0,0,0	monster	High Wizard Kathryne	1651,1,6000000,1800000,1
//@*/

I have a number of such overrides already written, but those will be part of a separate release to keep this PR shorter.

Issues addressed: N/A

Conditional comments are similar to #ifdef directives in the C
preprocessor, allowing to exclude parts of a script depending on whether
a certain flag is enabled or not.

The feature is designed to offer backward compatibility to scripts that
use it, letting older engines seamlessly ignore the conditional part as
if it was a comment.

The structure of a conditional comment is the following:

// The following block is only parsed if FLAG is defined. If not
// defined, or when loaded by an older engine, the block is treated as a
// block comment.

/*@Flag
.@x = 1;
//@*/

// The following block is only parsed if FLAG is not defined. If
// defined, or when loaded by an older engine, the block is treated as a
// block comment.

/*@!FLAG
.@x = 1;
//@*/

// The following block is only parsed if FLAG is defined. If not
// defined, the block is treated as a block comment. When loaded by an
older engine, the block is also parsed.

/*@Flag*/
.@x = 1;
//@*/

// The following block is only parsed if FLAG is not defined. If
// defined, the block is treated as a block comment. When loaded by an
// older engine, the block is also parsed.

/*@!FLAG*/
.@x = 1;
//@*/

The following flags are currently always defined:

- TRUE
- HERCULES

The following flag is currently never defined and can be used to
temporarily disable blocks of code:

- FALSE

The following flags are currently conditionally defined, based on the
renewal/pre-renewal compile time toggle:

- RENEWAL
- PRERENEWAL

Plugins can define conditional flags at runtime by calling
script->declare_conditional_feature("FLAG_NAME"), allowing scripts to
behave differently when a plugin is loaded.

Signed-off-by: Haru <[email protected]>
This provides an easy way for people that think those scripts are a
security hazard to disable them completely.

This is based on the conditional comment syntax checking for the
LOADGMSCRIPTS flag. Third party scripts can also put their
administrative bits under LOADGMSCRIPTS conditional blocks in order to
let this setting skip them if desired.

Signed-off-by: Haru <[email protected]>
@MishimaHaruna MishimaHaruna added the component:core:scriptengine Affecting the script engine or the script commands label Oct 30, 2021
@MishimaHaruna MishimaHaruna added this to the Release v2021.11.03 milestone Oct 30, 2021
@MishimaHaruna MishimaHaruna added the hacktoberfest-accepted Easy-to-tackle issues label Oct 31, 2021
@hemagx hemagx merged commit 419fc20 into HerculesWS:master Oct 31, 2021
@MishimaHaruna MishimaHaruna deleted the conditional-comments branch October 31, 2021 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:core:scriptengine Affecting the script engine or the script commands hacktoberfest-accepted Easy-to-tackle issues
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants