Skip to content

Commit

Permalink
Merge pull request #2556 from ivan-mogilko/362--testmissinginteractions
Browse files Browse the repository at this point in the history
Editor: test missing event handlers as a post-compilation step
  • Loading branch information
ivan-mogilko authored Nov 7, 2024
2 parents 9cf99a3 + 17c2430 commit 874b459
Show file tree
Hide file tree
Showing 33 changed files with 853 additions and 202 deletions.
14 changes: 8 additions & 6 deletions Common/game/interactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,17 @@ std::unique_ptr<InteractionEvents> InteractionEvents::CreateFromStream_v362(Stre

void InteractionEvents::Read_v361(Stream *in)
{
Events.clear();
const size_t evt_count = in->ReadInt32();
for (size_t i = 0; i < evt_count; ++i)
{
Events.push_back(String::FromStream(in));
Events.push_back( { String::FromStream(in) } );
}
}

HError InteractionEvents::Read_v362(Stream *in)
{
Events.clear();
InteractionEventsVersion ver = (InteractionEventsVersion)in->ReadInt32();
if (ver != kInterEvents_v362)
return new Error(String::FromFormat("InteractionEvents version not supported: %d", ver));
Expand All @@ -62,17 +64,17 @@ HError InteractionEvents::Read_v362(Stream *in)
const size_t evt_count = in->ReadInt32();
for (size_t i = 0; i < evt_count; ++i)
{
Events.push_back(StrUtil::ReadString(in));
Events.push_back( { StrUtil::ReadString(in) } );
}
return HError::None();
}

void InteractionEvents::Write_v361(Stream *out) const
{
out->WriteInt32(Events.size());
for (const auto &fn : Events)
for (const auto &evt : Events)
{
fn.Write(out);
evt.FunctionName.Write(out);
}
}

Expand All @@ -81,9 +83,9 @@ void InteractionEvents::Write_v362(Stream *out) const
out->WriteInt32(kInterEvents_v362);
StrUtil::WriteString(ScriptModule, out);
out->WriteInt32(Events.size());
for (const auto &fn : Events)
for (const auto &evt : Events)
{
StrUtil::WriteString(fn, out);
StrUtil::WriteString(evt.FunctionName, out);
}
}

Expand Down
17 changes: 16 additions & 1 deletion Common/game/interactions.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,22 @@ struct InteractionEvents
{
// An optional name of a script module to run functions in
String ScriptModule;
std::vector<String> Events;
// Script function names, corresponding to the event's index,
// paired with Enabled flag to tell if this event handler has to be processed
struct EventHandler
{
String FunctionName;
// At runtime we may want to receive function's call result and update
// Enabled status, but result may be delayed, so we have to use a shared memory object for safety.
// TODO: have this in runtime-only struct, when we have a clear separation
std::shared_ptr<bool> Enabled;

inline bool IsEnabled() const { return Enabled && *Enabled; }

EventHandler(const String &fn_name)
: FunctionName(fn_name), Enabled(new bool(!fn_name.IsEmpty())) {}
};
std::vector<EventHandler> Events;

// Read and create pre-3.6.2 version of the InteractionEvents
static std::unique_ptr<InteractionEvents> CreateFromStream_v361(Stream *in);
Expand Down
35 changes: 26 additions & 9 deletions Editor/AGS.Editor/AGSEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@ public class AGSEditor
public event PreDeleteSpriteHandler PreDeleteSprite;
public delegate void ProcessAllGameTextsHandler(IGameTextProcessor processor, CompileMessages errors);
public event ProcessAllGameTextsHandler ProcessAllGameTexts;
public delegate void ExtraCompilationStepHandler(CompileMessages errors);
public delegate void ExtraCompilationStepHandler(CompilationStepArgs args);
public event ExtraCompilationStepHandler ExtraCompilationStep;
public delegate void ExtraOutputCreationStepHandler(bool miniExeForDebug);
public delegate void ExtraOutputCreationStepHandler(OutputCreationStepArgs args);
public event ExtraOutputCreationStepHandler ExtraOutputCreationStep;
public delegate void TestGameScriptsHandler(GenericMessagesArgs args);
public event TestGameScriptsHandler TestGameScripts;

public const string BUILT_IN_HEADER_FILE_NAME = "_BuiltInScriptHeader.ash";
public const string OUTPUT_DIRECTORY = "Compiled";
public const string DATA_OUTPUT_DIRECTORY = "Data"; // subfolder in OUTPUT_DIRECTORY for data file outputs
public const string DEBUG_OUTPUT_DIRECTORY = "_Debug";
//public const string DEBUG_EXE_FILE_NAME = "_debug.exe";
public const string GAME_FILE_NAME = "Game.agf";
public const string BACKUP_EXTENSION = "bak";
public const string OLD_GAME_FILE_NAME = "ac2game.dta";
Expand Down Expand Up @@ -991,7 +992,7 @@ private object CompileScripts(IWorkProgress progress, object parameter)
{
headers.Add(scripts.Header);
CompileScript(scripts.Script, headers, errors);
_game.ScriptsToCompile.Add(scripts);
_game.ScriptsToCompile.Add(scripts);
}

CompileScript(dialogScripts, headers, errors);
Expand All @@ -1002,12 +1003,28 @@ private object CompileScripts(IWorkProgress progress, object parameter)
errorToReturn = ex;
}

if (ExtraCompilationStep != null)
ExtraCompilationStep?.Invoke(new CompilationStepArgs(_game.ScriptsToCompile, errors));

RunGameScriptTests(errors);

return errorToReturn;
}

/// <summary>
/// Runs few optional script checks.
/// </summary>
private void RunGameScriptTests(CompileMessages errors)
{
// Update autocomplete for all the script modules, if necessary
// TODO: this is not a ideal solution, as it's likely duplicating script parsing by the script compiler
// if done right after the compilation. Search for a better way later?
foreach (ScriptAndHeader scripts in _game.RootScriptFolder.AllItemsFlat)
{
ExtraCompilationStep(errors);
if (!scripts.Script.AutoCompleteData.Populated)
AutoComplete.ConstructCache(scripts.Script, null);
}

return errorToReturn;
TestGameScripts.Invoke(new GenericMessagesArgs(errors));
}

private void CreateAudioVOXFile(bool forceRebuild)
Expand Down Expand Up @@ -1067,7 +1084,7 @@ private object CreateCompiledFiles(IWorkProgress progress, object parameter)
targetDataFile.Build(errors, forceRebuild); // ensure that data file is built first
if (ExtraOutputCreationStep != null)
{
ExtraOutputCreationStep(false);
ExtraOutputCreationStep(new OutputCreationStepArgs(false));
}

// TODO: As of now the build targets other than DataFile and Debug do DEPLOYMENT rather than BUILDING
Expand Down Expand Up @@ -1323,7 +1340,7 @@ private void CreateMiniEXEForDebugging(CompileMessages errors)
target.Build(errors, false);
if (ExtraOutputCreationStep != null)
{
ExtraOutputCreationStep(true);
ExtraOutputCreationStep(new OutputCreationStepArgs(true));
}

buildNames[target.Name] = Factory.AGSEditor.BaseGameFileName;
Expand Down
8 changes: 5 additions & 3 deletions Editor/AGS.Editor/AGSEditor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,6 @@
</Compile>
<Compile Include="ColorThemes\Controls\MainMenuColorTable.cs" />
<Compile Include="ColorThemes\Controls\ToolStripColorTable.cs" />
<Compile Include="Components\CharacterIDChangedEventArgs.cs" />
<Compile Include="Components\CharacterRoomChangedEventArgs.cs" />
<Compile Include="Components\CharactersComponent.cs" />
<Compile Include="Components\CursorsComponent.cs" />
<Compile Include="Components\DebugLogComponent.cs" />
Expand All @@ -152,14 +150,19 @@
<Compile Include="Components\TextParserComponent.cs" />
<Compile Include="Components\ViewsComponent.cs" />
<Compile Include="Config\ConfigUtils.cs" />
<Compile Include="Entities\CharacterIDChangedEventArgs.cs" />
<Compile Include="Entities\CharacterRoomChangedEventArgs.cs" />
<Compile Include="Entities\CompilationStepArgs.cs" />
<Compile Include="Entities\ExistingScriptHeaderToAdd.cs" />
<Compile Include="Entities\ExistingFileToAdd.cs" />
<Compile Include="Config\IObjectConfig.cs" />
<Compile Include="Config\IObjectConfigurable.cs" />
<Compile Include="Config\ObjectConfigJson.cs" />
<Compile Include="Entities\LogBufferEventArgs.cs" />
<Compile Include="Entities\MultiSelectAction.cs" />
<Compile Include="Entities\OutputCreationStepArgs.cs" />
<Compile Include="Entities\ScriptModuleDef.cs" />
<Compile Include="Entities\GenericMessagesArgs.cs" />
<Compile Include="GUI\AssignToView.cs">
<SubType>Form</SubType>
</Compile>
Expand Down Expand Up @@ -1042,7 +1045,6 @@
<Compile Include="Enums\DebugState.cs" />
<Compile Include="Enums\FunctionCallType.cs" />
<Compile Include="Entities\RecentlyEditedGame.cs" />
<Compile Include="Enums\ZoomToFileZoomType.cs" />
<Compile Include="Factory.cs" />
<Compile Include="GIFLoader\GifDecoder.cs" />
<Compile Include="GUI\AboutDialog.cs">
Expand Down
28 changes: 20 additions & 8 deletions Editor/AGS.Editor/Components/BaseComponentWithScripts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,32 @@ public BaseComponentWithScripts(GUIController guiController, AGSEditor agsEditor

protected void ZoomToCorrectPositionInScript(ScriptEditor editor, ZoomToFileEventArgs evArgs)
{
if (editor == null)
{
return;
}

bool result = true;
if (evArgs.ZoomType == ZoomToFileZoomType.ZoomToCharacterPosition)
{
editor.GoToLineOfCharacterPosition(evArgs.ZoomPosition, evArgs.SelectLine);
result = editor.GoToLineOfCharacterPosition(evArgs.ZoomPosition, evArgs.SelectLine);
}
else if (evArgs.ZoomType != ZoomToFileZoomType.DoNotMoveCursor)
{
if (evArgs.ZoomToText != null)
{
evArgs.ZoomPosition = editor.GetLineNumberForText(evArgs.ZoomToText);
switch (evArgs.MatchStyle)
{
case ZoomToFileMatchStyle.MatchExact:
evArgs.ZoomPosition = editor.GetLineNumberForText(evArgs.ZoomToText, true);
break;
case ZoomToFileMatchStyle.MatchRegex:
evArgs.ZoomPosition = editor.GetLineNumberForPattern(evArgs.ZoomToText, true);
break;
default:
evArgs.ZoomPosition = -1;
break;
}
}
editor.GoToLine(evArgs.ZoomPosition, evArgs.SelectLine, evArgs.ZoomToLineAfterOpeningBrace);

result =
(evArgs.ZoomPosition >= 0 &&
editor.GoToLine(evArgs.ZoomPosition, evArgs.SelectLine, evArgs.ZoomToLineAfterOpeningBrace));

if (evArgs.IsDebugExecutionPoint)
{
Expand All @@ -43,6 +53,8 @@ protected void ZoomToCorrectPositionInScript(ScriptEditor editor, ZoomToFileEven
}
}
}

evArgs.Result = result ? ZoomToFileResult.Success : ZoomToFileResult.LocationNotFound;
}

protected abstract ContentDocument GetDocument(ScriptEditor editor);
Expand Down
17 changes: 17 additions & 0 deletions Editor/AGS.Editor/Components/CharactersComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public CharactersComponent(GUIController guiController, AGSEditor agsEditor)
_guiController.RegisterIcon(ICON_KEY, Resources.ResourceManager.GetIcon("charactr.ico"));
_guiController.RegisterIcon("CharacterIcon", Resources.ResourceManager.GetIcon("charactr-item.ico"));
_guiController.ProjectTree.AddTreeRoot(this, TOP_LEVEL_COMMAND_ID, "Characters", ICON_KEY);
_agsEditor.TestGameScripts += ScanAndReportMissingInteractionHandlers;
RePopulateTreeView();
}

Expand Down Expand Up @@ -369,5 +370,21 @@ protected override string GetFolderDeleteConfirmationText()
{
return "Are you sure you want to delete this folder and all its characters?" + Environment.NewLine + Environment.NewLine + "If any of the characters are referenced in code by their number it could cause crashes in the game.";
}

private void ScanAndReportMissingInteractionHandlers(GenericMessagesArgs args)
{
var errors = args.Messages;
foreach (Character c in _agsEditor.CurrentGame.Characters)
{
var missing = _agsEditor.Tasks.TestMissingInteractionHandlers(c.Interactions);
if (missing == null || missing.Count == 0)
continue;

foreach (var miss in missing)
{
errors.Add(new CompileWarning($"Character ({c.ID}) {c.ScriptName}'s event {c.Interactions.Schema.FunctionSuffixes[miss]} function \"{c.Interactions.ScriptFunctionNames[miss]}\" not found in script {c.Interactions.ScriptModule}."));
}
}
}
}
}
12 changes: 11 additions & 1 deletion Editor/AGS.Editor/Components/DialogsComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ private void GUIController_OnZoomToFile(ZoomToFileEventArgs evArgs)
RemoveExecutionPointFromAllScripts();
}

if (evArgs.Handled)
{
return; // operation has been completed by another handler
}

Dialog dialog = GetDialog(evArgs.FileName);
if (dialog != null)
{
Expand All @@ -320,7 +325,12 @@ private void GUIController_OnZoomToFile(ZoomToFileEventArgs evArgs)
dialogEditor.Paint += paintEvent;
dialogEditor.Invalidate();
}
}
evArgs.Result = ZoomToFileResult.Success;
}
else
{
evArgs.Result = ZoomToFileResult.ScriptNotFound;
}
}

private string GetNodeID(Dialog item)
Expand Down
Loading

0 comments on commit 874b459

Please sign in to comment.