Skip to content

Commit

Permalink
Fix bug #1251 (#95)
Browse files Browse the repository at this point in the history
Fix bug #1251: Load System.ocg also from non-empty object folders

Added test scenario for group loading logic, too.
  • Loading branch information
gitMarky committed Apr 6, 2019
1 parent bb80549 commit d7308ab
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 2 deletions.
6 changes: 6 additions & 0 deletions docs/sdk/definition/index.xml
Expand Up @@ -23,6 +23,11 @@
<dt><img height="16" src="../../images/icon_text.png" width="16"/><emlink href="definition/script.html">Script.c</emlink></dt>
<dd>
<text>The object script. See <emlink href="definition/script.html">object scripts</emlink> and <emlink href="lang.html">localization</emlink>.</text>
</dd>
<dt id="Systemocg"><img height="16" src="../../images/icon_system.png" width="16"/>System.ocg</dt>
<dd>
<text>Definitions can contain a system folder with any number of script files (*.c). These can define or modify global functions, or append to loaded definitions.</text>
<text>Scripts for system folders are only loaded from definition groups (i.e. *.ocd folders without DefCore.txt) before OC 9.0.</text>
</dd>
</dl>
<h id="RasterGraphics">Raster Graphics</h>
Expand Down Expand Up @@ -89,4 +94,5 @@
</part>
<author>Sven2</author><date>2002-04</date>
<author>matthes</author><date>2004-06</date>
<author>Marky</author><date>2019-04</date>
</doc>
5 changes: 5 additions & 0 deletions docs/sdk/folder/index.xml
Expand Up @@ -60,6 +60,10 @@ US:My scenario collection</code> <text>Important: No quotation marks and
<dd>
<text>Scenario folders can contain local music files. Also see <emlink href="scenario/index.html#Musicocg">scenarios</emlink>. The <emlink href="scenario/index.html#UeberladungenOCS">overloading rules</emlink> apply.</text>
</dd>
<dt id="Systemocg"><img height="16" src="../../images/icon_system.png" width="16"/>System.ocg</dt>
<dd>
<text>Scenario folders can contain any number of script files (*.c). These can define or modify global functions, or append to loaded definitions. The <emlink href="scenario/index.html#UeberladungenOCS">overloading rules</emlink> apply.</text>
</dd>
<dt id="ocd"><img height="16" src="../../images/icon_object_definition.png" width="16"/><emlink href="definition/index.html">*.ocd</emlink></dt>
<dd>
<text>Scenario folders can contain object definitions, which will then be available in all scenarios contained in the folder.Also see <emlink href="scenario/index.html#ocd">scenarios</emlink>. The <emlink href="scenario/index.html#UeberladungenOCS">overloading rules</emlink> apply.</text>
Expand All @@ -75,4 +79,5 @@ US:My scenario collection</code> <text>Important: No quotation marks and
</dl>
</part>
<author>Sven2</author><date>2006-09</date>
<author>Marky</author><date>2019-04</date>
</doc>
5 changes: 5 additions & 0 deletions docs/sdk/scenario/index.xml
Expand Up @@ -94,6 +94,10 @@ US:Attack of the Killer Wipfs</code>
<dd>
<text>Scenarios can overload individual entries of the global Graphics.ocg group as well as add new content like a sky background image or loader graphics (*.png or *.jpg). Sky graphics should either be tileable or big enough that it won't even tile if the player zooms out very far. Loader screen are displayed while the scenario is loading. If multiple files are present (Loader1.png, Loader2.png, Loader3.png...), one is selected randomly. You can optionally specify which loader to use in Scenario.txt. Loader image files located in parent scenario folders are also used.See <a href="#UeberladungenOCS">overloading rules</a>.</text>
</dd>
<dt id="Systemocg"><img height="16" src="../../images/icon_system.png" width="16"/>System.ocg</dt>
<dd>
<text>Scenarios can contain any number of script files (*.c). These can define or modify global functions, or append to loaded definitions. See <a href="#UeberladungenOCS">overloading rules</a>.</text>
</dd>
<dt id="Namestxt"><img height="16" src="../../images/icon_text.png" width="16"/>Names.txt</dt>
<dd>
<text>These names are used for clonks created in this scenario.</text>
Expand Down Expand Up @@ -125,4 +129,5 @@ US:Attack of the Killer Wipfs</code>
</part>
<author>Sven2</author><date>2002-04</date>
<author>matthes</author><date>2004-06</date>
<author>Marky</author><date>2019-04</date>
</doc>
@@ -0,0 +1,5 @@
[DefCore]
id=DummyDef
Version=8,0
Category=C4D_StaticBack
HideInCreator=true
@@ -0,0 +1,9 @@

/**
Dummy function, only exists to see if it gets loaded.
*/
global func FooFromDefinition()
{
Log("FooFromDefinition was loaded");
return true;
}
@@ -0,0 +1,9 @@

/**
Dummy function, only exists to see if it gets loaded.
*/
global func FooInNormalDefinition()
{
Log("FooInEmptyDefinition was loaded");
return true;
}
@@ -0,0 +1,9 @@

/**
Dummy function, only exists to see if it gets loaded.
*/
global func FooInNestedEmptyDefinition()
{
Log("FooInNestedEmptyDefinition was loaded");
return true;
}
@@ -0,0 +1,9 @@

/**
Dummy function, only exists to see if it gets loaded.
*/
global func FooInEmptyDefinition()
{
Log("FooInEmptyDefinition was loaded");
return true;
}
5 changes: 5 additions & 0 deletions planet/Tests.ocf/FileSystem.ocs/Scenario.txt
@@ -0,0 +1,5 @@
[Head]
Title=OC File System

[Landscape]
NoScan=1
275 changes: 275 additions & 0 deletions planet/Tests.ocf/FileSystem.ocs/Script.c
@@ -0,0 +1,275 @@
/**
Groups
Tests for loading data from the file system, name spaces, etc.
@author Marky
*/


func InitializePlayer(int player)
{
// Set zoom to full map size.
SetPlayerZoomByViewRange(player, LandscapeWidth(), nil, PLRZOOM_Direct);

// No FoW to see everything happening.
SetFoW(false, player);

// Move player to the start of the scenario.
GetCrew(player)->SetPosition(265, 180);
LaunchTest(1);
}


/* --- Test Control --- */

// Aborts the current test and launches the specified test instead.
global func LaunchTest(int nr)
{
// Get the control test.
var test = CurrentTest();
if (test)
{
// Finish the currently running test.
Call(Format("~Test%d_OnFinished", test.testnr));
// Start the requested test by just setting the test number and setting
// test.launched to false, effect will handle the rest.
}
else
{
// Create a new control effect and launch the test.
test = CurrentTest(true);
test.player = GetPlayerByIndex(0, C4PT_User);
test.global_result = true;
test.count_total = 0;
test.count_failed = 0;
test.count_skipped = 0;
}

test.testnr = nr;
test.launched = false;
}


// Calling this function skips the current test, does not work if last test has been ran already.
global func SkipTest()
{
// Get the control test.
var test = CurrentTest();
if (test)
{
// Finish the previous test.
Call(Format("~Test%d_OnFinished", test.testnr));
// Start the next test by just increasing the test number and setting
// test.launched to false, effect will handle the rest.
test.testnr++;
test.launched = false;
test.count_skipped++;
}
}


/* --- Test Effect --- */

static const IntTestControl = new Effect
{
Start = func (int temporary)
{
if (!temporary)
{
// Set default interval.
this.Interval = 1;
}
return FX_OK;
},

Timer = func ()
{
// Launch new test if needed.
if (!this.launched)
{
// Start the test if available, otherwise finish test sequence.
if (!this->HasNextTest())
{
Log("Test %d not available, the previous test was the last test.", this.testnr);
Log("=====================================");
Log("All tests have been completed!");
Log("* %d tests total", this.count_total);
Log("%d tests failed", this.count_failed);
Log("%d tests skipped", this.count_skipped);
Log("=====================================");
if (this.count_skipped == 0 && this.count_failed == 0 && this.count_total > 0)
{
Log("All tests passed!");
}
else
{
Log("At least one test failed or was skipped!");
}
return FX_Execute_Kill;
}
// Log test start.
Log("=====================================");
Log("Test %d started:", this.testnr);
this.launched = true;
this.count_total++;
this.current_result = false;
this.current_check = true;
}

// waiting
if (this.wait > 0)
{
this.wait -= 1;
return FX_OK;
}

// Check whether the current test has been finished.
if (this->ExecuteTest())
{
this.launched = false;

if (this.current_result)
{
Log(">> Test %d passed.", this.testnr);
}
else
{
Log(">> Test %d failed.", this.testnr);
this.count_failed++;
}

// Update global result
this.global_result &= this.current_result;

// Call the test on finished function.
this->CleanupTest();
// Log result and increase test number.
Log("Test %d successfully completed.", this.testnr);
this.testnr++;
}
return FX_OK;
},


GetIndex = func () // Get the index of the test
{
return this.testnr - 1;
},

GetNumber = func () // Get the test number
{
return this.testnr;
},


HasNextTest = func ()
{
return !!Scenario[Format("Test%d_Execute", this.testnr)];
},


ExecuteTest = func ()
{
return Call(Format("Test%d_Execute", this.testnr));
},


CleanupTest = func ()
{
Call(Format("~Test%d_OnFinished", this.testnr));
},
};


global func CurrentTest(bool create)
{
if (create)
{
return Scenario->CreateEffect(IntTestControl, 100, 2);
}
else
{
return GetEffect("IntTestControl", Scenario);
}
}

global func Evaluate()
{
var test = CurrentTest();
test.current_result = test.current_check;
return true;
}

global func DoTest(string description, bool value)
{
Log(description);
if (value)
{
return PassTest();
}
else
{
return FailTest();
}
}


global func PassTest()
{
CurrentTest().current_result = true;
return true;
}


global func FailTest()
{
CurrentTest().current_result = false;
return true;
}


global func Wait(int amount)
{
CurrentTest().wait = Max(0, amount ?? 10);
return false;
}


/*-- The actual tests --*/

// Might be pointless, because the parser already gives a warning/error
// if you write a plain function call Foo()

global func Test1_Execute()
{
return DoTest("Function from *.c in *\\System.ocg should get loaded (first file in that folder), you should see a log message above.", this->~Foo001());
}

global func Test2_Execute()
{
return DoTest("Function from *.c in *\\System.ocg should get loaded (other files on that folder), you should see a log message above.", this->~Foo002());
}

global func Test3_Execute()
{
return DoTest("Function from *.c in empty *\\*.ocd\\System.ocg should get loaded, you should see a log message above.", this->~FooInEmptyDefinition());
}

global func Test4_Execute()
{
return DoTest("Function from *.c in empty nested *\\*.ocd\\*.ocd\\System.ocg should get loaded, you should see a log message above.", this->~FooInNestedEmptyDefinition());
}

global func Test5_Execute()
{
return DoTest("Function from *.c in normal *.ocd definition *\\*.ocd\\System.ocg should get loaded, you should see a log message above.", this->~FooInNormalDefinition());
}

global func Test6_Execute()
{
// Well, nothing would work without this, not even the scenario, but let's test it anyway
return DoTest("Function from Script.c in normal *.ocd definition *\\*.ocd should get loaded, you should see a log message above.", this->~FooFromDefinition());
}


9 changes: 9 additions & 0 deletions planet/Tests.ocf/FileSystem.ocs/System.ocg/Foo001.c
@@ -0,0 +1,9 @@

/**
Dummy function, only exists to see if it gets loaded.
*/
global func Foo001()
{
Log("Foo001 was loaded");
return true;
}
9 changes: 9 additions & 0 deletions planet/Tests.ocf/FileSystem.ocs/System.ocg/Foo002.c
@@ -0,0 +1,9 @@

/**
Dummy function, only exists to see if it gets loaded.
*/
global func Foo002()
{
Log("Foo002 was loaded");
return true;
}

0 comments on commit d7308ab

Please sign in to comment.