IDE Setup and Compile Checking
Get IntelliSense for server scripts in VS Code, Rider or Visual Studio with the generated companion project, compile-check before launch, and read server logs.
10 min read
Why scripts need a companion project#
Server scripts and modules are loose .cs files under server/scripts/ and server/modules/. The game server compiles each one itself at startup, prepending a hidden block of using directives so ScriptPlayer, ModuleLoader and TaskScript resolve without you writing anything. That is convenient in the Script Editor, which knows about the prelude and offers autocomplete from the generated API database, but a general-purpose C# editor opening one of those files sees an unresolvable soup: no completion, no go-to-definition, and no error until the game boots and the server log reports a compile failure.
The IDE companion project fixes that. generate_script_project writes a real .csproj plus a global-usings file into the project's server/ folder, so any C# IDE resolves engine types and your own module types while you edit. Building that project is also a genuine pre-flight check: script mistakes become build errors at your desk instead of log lines after launch.
Two facts to keep in mind:
- The running server never reads these files. They exist only for your IDE. The server keeps compiling
scripts/**andmodules/**on its own at runtime. - They are never shipped. Push to Test and Publish zip only
server/data,server/modules,server/scriptsandserver/schemas. The Script Editor's file tree also hides them (.g.cs,.csproj,.props,.targets,.slnfiles and theobj/binfolders), so they never show up as game content.
What gets written#
| File | Purpose |
|---|---|
server/VastopiaScripts.csproj | A .NET 8 class-library project that compiles scripts/**/*.cs and modules/**/*.cs (skipping scripts/**/.compile_cache/) and references the engine assembly RealmServer.dll. Assembly name VastopiaScripts.IntelliSense. |
server/_VastopiaGlobalUsings.g.cs | The engine's script and module preludes as global using lines (33 of them) - the same namespaces the server injects at runtime, which is why the IDE resolves the same names the server does. |
server/Directory.Build.props | Redirects MSBuild's obj/ output into server/.ide/obj/ so no build artefacts appear next to your scripts. |
server/.ide/ | Where bin/ and obj/ land when you build. Safe to delete at any time. |
The project sets Nullable to annotations (so T? works without warnings), disables implicit usings (the prelude file replaces them), and silences warnings that are noise for script fragments: CS0168, CS0169, CS0414, CS1591, CS8019, CS8632.
Prerequisites#
-
The .NET 8 SDK. The editor's own Dev Test needs it too; if
dotnet --versiondoes not print a version, install it from dot.net. -
The engine assembly,
RealmServer.dll. The csproj probes these locations in order and uses the first that exists:server/builtin-assemblies/RealmServer.dllinside your project;%USERPROFILE%\Documents\GitHub\Vastopia-Server\Server\bin\Debug\net8.0\RealmServer.dll;..\..\..\GitHub\Vastopia-Server\Server\bin\Debug\net8.0\RealmServer.dllrelative toserver/.
If none exists, the build prints the warning Vastopia engine assembly not found - IntelliSense cannot resolve engine types and every engine type shows as unresolved. Either pass the path explicitly (
dotnet build -p:VastopiaServerDll=C:\path\to\RealmServer.dll), or drop a copy of the DLL intoserver/builtin-assemblies/.
If you have the server source checkout linked in Settings > Editor > Source Checkouts > Server project (the folder holding RealmServer.csproj), the simplest way to produce the DLL is to press Dev Test once: the editor runs dotnet build on that checkout and the output lands in bin/Debug/net8.0/RealmServer.dll, exactly where the second probe looks.
Generate the project#
generate_script_project is an MCP tool, so you run it from your AI assistant with the project selected (see MCP setup). There is no button for it in the editor.
- Ask the assistant to run
generate_script_project. With no arguments it writes the three files and lets the csproj probe for the engine DLL. The result reportsaction: "create"the first time and"update"on later runs. - To pin the engine assembly, pass
server_dllwith the full path toRealmServer.dll. - To leave noisy files out of the IDE compile, pass
excludewith a semicolon-separated list of paths relative toserver/, for examplemodules\Content\Drops\ItemDropModule.cs. The value lands in the csproj'sVastopiaExcludeproperty, which you can also edit by hand later. - Pass
dryRun: trueto preview the files without writing them.
Re-run the tool whenever the engine prelude changes (the generated usings file says so in its header); the three files are regenerated in place.
Open the project in your IDE#
Open the server/ folder, or server/VastopiaScripts.csproj directly, in VS Code (with the C# extension), JetBrains Rider or Visual Studio. You get:
- Completion on the script API (
player.,ScriptWorld.,npc.Combat.) and on your own modules, includingFishingModule.Instance.for any module declared asModuleBase<FishingModule>. - Go-to-definition into the engine assembly's types and into your modules.
- Red squiggles on real errors as you type.
Keep editing files where they already are: the IDE compiles scripts/** and modules/** in place, so there is nothing to copy or sync, and the Script Editor works on the same files. If you save a file from the Script Editor after changing it in your IDE, the editor notices the file was modified on disk and asks before overwriting it (File Changed on Disk, with an Overwrite button).
Compile-check before launch#
Building the companion project compiles every script and module against the real engine assembly:
cd <your-project>/server
dotnet build VastopiaScripts.csproj
A clean build means every file parses and every engine call resolves. Read the output like any C# build: each error line names the file, line and column, the CSxxxx code and the message.
One fidelity gap, on purpose#
The server compiles each script file and each module file separately, in its own assembly. The companion project compiles them all together. The IDE is therefore slightly more permissive than the runtime in two specific ways:
| Symptom | Cause | Fix |
|---|---|---|
Builds clean in the IDE, but the server log shows error CS0103: The name 'X' does not exist | A helper class or method declared in one script file is used from another. The IDE can see across files; the server cannot. | Move shared logic into a module under server/modules/. Modules are referencable from every script via ModuleLoader.Module<T>() or T.Instance. |
The server is happy, but the IDE reports error CS0101: The namespace already contains a definition for 'X' | Two files declare the same top-level type name. At runtime they live in separate assemblies, so there is no clash. | Rename one, or add the file to VastopiaExclude in the csproj (or the tool's exclude argument) to quiet the IDE. |
Both are IDE-only artefacts; the runtime behaviour is the one that ships. See Scripting overview for the compile-per-file rule.
What Dev Test checks for you#
You do not have to remember to build before testing. Pressing Dev Test saves the project, then validates every .cs file under server/scripts/ and server/modules/ in two passes before the server starts:
- Structural lint - instant and dependency-free. Catches mismatched or unclosed
( ) { } [ ], unterminated strings, characters and block comments, and a{opened while anif/while/for/foreach/switch/catch/using/lockcondition is still missing its). If anything is found, the compiler pass is skipped. - Compiler pass - runs only when no structural errors were found and the server checkout has been built (
bin/Debug/net8.0/RealmServer.dllexists). It builds a throwaway project over all the scripts on a background thread and keeps only parser-level diagnostics (codesCS1000-CS1999, such as; expectedor) expected). Semantic codes are deliberately dropped here because the editor cannot reproduce the server's exact reference set, so they would be false positives - that is what the companion project is for.
If errors are found, the Script Validation dialog opens instead of the server: a summary line (N errors found in your scripts), a status note, and a tree of problems grouped by file as Line n:m - message [code]. Double-click a problem to jump to that line in the Script Editor. Press Launch Anyway to start the server regardless, or Close to fix things first. When the compiler pass could not run you will see Server not built yet - only structural checks ran. with the tip to run Dev Test once, because that first run builds the server.
Reading server logs#
The server reports compile problems at startup and never crashes on them: a script that fails to compile is logged and skipped, and the rest of the game boots without it. That is why a feature "silently not working" usually means one log line you have not read yet.
The local Dev Test server#
On Windows, Dev Test launches the server in its own Command Prompt window titled EzRealm Server (Play Local), running dotnet RealmServer.dll <port> with the working directory set to your project's server/ folder and DEV_ALL_ADMIN=1 (so built-in admin commands work while testing). Keep that window visible. The lines that matter:
| Log line | Meaning |
|---|---|
[ScriptManager] Compiling: Foo.cs | A script is being compiled. |
[ScriptManager] Compilation errors in Foo.cs: followed by one indented line per error with its line, column, CSxxxx code and message | That script failed and was skipped. Fix and relaunch. |
[ModuleLoader] Compilation errors in Foo.cs: | Same, for a module. Any script calling ModuleLoader.Module<Foo>() now gets null. |
[ScriptManager] Script loading complete: followed by counts (Commands, Object Scripts, NPC Scripts, NPC Combat Scripts, Item Scripts, Equipment Scripts, Special Attacks, Location Scripts, Buffs, Interface Scripts, Task Scripts, World Task Scripts) | Everything loaded; if your new command is not counted, it did not compile or is in the wrong folder. |
[ScriptManager] Compile cache: N hit(s), M compiled | How many scripts came from the compile cache versus fresh compiles. |
[ModuleLoader] Enabled: <Module Name> | A module passed OnEnable. |
[ModuleLoader] Game module 'x' overrides the pre-compiled platform version. | Your project ships its own copy of a platform system; yours wins. |
[ModuleLoader] Game module 'x' is disabled by data/systems.json - not loading. | The system is switched off in Game Config; the file is ignored. |
[ScriptManager] Warning: Combat script 'Foo' not found for NPC 12 (Name) | An NPC's combat_script_path names a class that did not compile or does not exist. |
Compiled scripts are cached under server/scripts/.compile_cache/ (override the location with the EZREALM_SCRIPT_CACHE_DIR environment variable). The cache key includes the script's content, so edits always recompile; delete the folder if you want to force a full recompile and see every file's result.
When a script is already running you can also log from it: player.Log("...") writes to the same console tagged with the player's username, and modules have Log, LogWarning and LogError.
Running the server by hand#
To check a project's scripts without the editor or a client, run the engine directly and watch the boot output:
cd <your-project>/server
dotnet "<Vastopia-Server>/Server/bin/Debug/net8.0/RealmServer.dll" 7991
Grep the output for Compilation errors and CS codes. Two lines are harmless noise on a developer machine: Control server failed to start ... Access is denied (the hot-reload HTTP listener needs elevated rights on Windows) and No pre-compiled assemblies dir (platform module binaries only exist in hosted containers). Stop the process when you are done; it runs until killed.
The platform test server#
After Play Local or Push to Test, your game runs on a platform test server, and its console is not on your machine. The Server Logs button at the top right of the editor opens a log view for the game linked to your project:
- Lines picks how many recent lines to fetch: 100, 200, 500 or 1000.
- Refresh fetches now; the view also refreshes on its own every 3 seconds while open.
- Auto-scroll keeps the newest line in view; Clear empties the display.
- Lines containing
error,exceptionorfatalare tinted red,warnyellow, andinfo,readyorlisteningblue.
The view reads the test deployment's logs, requires you to be signed in to the editor, and needs the project to be linked to a game (it reports No project open or no game ID configured otherwise). Play Local switches to it automatically once the client is launched. See Test your game for the difference between Dev Test, Play Local and Push to Test.
Troubleshooting#
| Problem | What to check |
|---|---|
| Every engine type is unresolved in the IDE | The Vastopia engine assembly not found warning: none of the probe paths exist. Build the server checkout (Dev Test does this) or pass -p:VastopiaServerDll=.... |
using Core.Game.Entities.Items; (or similar) does not resolve | The prelude has no using EzRealm.Server;, so partial namespaces never resolve at runtime even if an IDE suggests them. Use the full EzRealm.Server.Core... name, or rely on the prelude and write no using at all. |
Random is ambiguous or has the wrong signature | The prelude's using static GameData brings GameData.Random(int, int) into scope. Use System.Random.Shared for the .NET class. |
ModuleLoader.Module<Foo>() returns null | The module failed to compile (see the [ModuleLoader] lines), its system is disabled in systems.json, or another game module already registered the same Id. |
| A script compiles but never runs | It is in the wrong folder for its base class (commands in scripts/commands/, NPC scripts in scripts/npcs/, and so on), or it is not bound to an entity id. See Script API reference. |
With AI (MCP)#
generate_script_project creates or refreshes the companion project (server_dll, exclude, dryRun). read_server_logs fetches a deployed game's logs - mode is test (default), live or beta; lines defaults to 200 and is capped at 2000; contains, exclude and min_level (debug, info, warn, error) filter the result, and lines with no level prefix are always kept so legacy output is never hidden. get_script_api returns the API surface so scripts are written against the real engine, and write_server_script generates compile-ready skeletons per script kind. Full list: MCP tools reference.
