Skip to main content

Example code: Lab 0

This is one way to meet the brief, not the only correct answer. Read it to compare with your own attempt and understand the trade-offs, not to copy a definitive template. Any approach that passes the validation checklist is just as valid.

← Back to the public project brief

Build the diagnostic DLL​

Create a Class Library (.NET Framework) targeting net48. Add a reference to the ScriptHookVDotNet3.dll from your game installation, add a C# file containing the code below, then build. Copy only the resulting P0Diagnostic.dll into the game's scripts/ folder. The installation guide covers the loader files that belong in the game root. This sample is also compiled by the site's verification harness against the real SDK; its behavior still needs your in-game check.

The code​

using System;
using System.IO;
using GTA;

public sealed class P0Diagnostic : Script
{
private const string LogPath = "scripts/p0_diagnostic.log";
private int _nextAnnouncement;

public P0Diagnostic()
{
Tick += OnTick;
Aborted += OnAborted;
}

private void OnTick(object sender, EventArgs e)
{
if (Game.GameTime < _nextAnnouncement)
{
return;
}

_nextAnnouncement = Game.GameTime + 5000;

WriteLog("diagnostic active");
GTA.UI.Screen.ShowSubtitle("Diagnostic: ScriptHookVDotNet is working.", 3000);
}

private void OnAborted(object sender, EventArgs e)
{
WriteLog("script unloaded");
}

private static void WriteLog(string message)
{
try
{
string line = $"{DateTime.Now:HH:mm:ss} P0 {message}{Environment.NewLine}";
File.AppendAllText(LogPath, line);
}
catch (Exception)
{
// The log is secondary: the subtitle is sufficient visual proof.
}
}
}

Key decisions​

  • Inheriting from GTA.Script: Any C# class loaded by ScriptHookVDotNet must inherit from GTA.Script. The bridge scans assemblies in scripts/ and instantiates these classes on reload.
  • Event attachment in constructor: Subscribing to Tick += OnTick; and Aborted += OnAborted; registers execution callbacks. Tick runs every frame, while Aborted runs when the script is unloaded or reloaded.
  • Time throttling with Game.GameTime: Tick runs dozens of times per second. Writing to disk or displaying subtitles on every frame would cause severe performance stutter. _nextAnnouncement = Game.GameTime + 5000; ensures code runs only once every 5 seconds.
  • Defensive logging with try-catch: Disk writes can fail if a file is locked by an editor or antivirus. Wrapping File.AppendAllText in try-catch prevents disk access errors from crashing the game.
  • Teardown notification: In OnAborted, logging "script unloaded" proves that ScriptHookVDotNet cleanly alerts running scripts before removing them from memory.