Skip to content

An open-source Source Engine replica, written in C#, capable of joining real Garry's Mod servers to some extent (very much so an alpha right now)

License

Notifications You must be signed in to change notification settings

marchc1/Source.NET

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Source.NET

Table of Contents

What is this?
Goals
Credits & Thanks
Structure

Stage 0: External Dependencies + Global Usings
Stage 1: Engine Common + Tier Libraries
Stage 2: Pre-Engine Components
Stage 3: Engine Main
Stage 4: Post-Engine Components
Stage 5: Game Realms
Stage 6: Executables

API Notes
Future Plans

What is this?

This is an open source clone of the Source Engine, based on RaphaelIT7's Source Engine branch, written in C#. It aims to be as compatible as possible with Source Engine's formats and protocols, more specifically targetting Garry's Mod compatibility.

Source Launcher_h58b4odIBv image

It can currently connect to real Garry's Mod servers. Development is currently done by connecting to a local Garry's Mod SRCDS instance - if you wish to contribute, you can set one up relatively easily with these instructions. Note that most entities aren't implemented yet - your best luck is a vanilla, -noworkshop -noaddons SRCDS server on gm_flatgrass.

Goals

I originally started this project mostly to learn more about the Source Engine, but due to public interest I've made it open-sourced in the case that it helps others and could be further worked on by people smarter than I (especially in the graphics department).

In an ideal world, it could serve as a playable Garry's Mod client - but that is a herculean task (and even that feels like an understatement) - and would require a lot more hands than just me to even be remotely feasible.

Credits & Thanks

  • Valve for obvious reasons
  • RaphaelIT7 for various contributions & unintentionally getting me down the rabbit-hole of Source Engine tomfoolery in the first place
  • maddnias for his work on SharpVPK, and antim0118 for his changes which fixed some incompatibilities
  • Leystryku for his work on leysourceengineclient - which has served as a very good baseline for networking implementations when I was struggling

Building

You need the following:

  • A C#-compatible IDE obviously (Visual Studio 2022 is what I use for now, although I'm sure it would work fine on Rider or VS Code)
  • The .NET 9.0 SDK
  • A license for Garry's Mod on Steam

You will also need to symlink the Half Life 2/Garry's Mod vpk files or copy them directly. A Powershell file is provided in Game.Assets to automatically symlink the necessary VPK's and map files - if someone wants to write an equivalent Linux script for doing this, that would be very appreciated.

Structure

The engine is very similar to Source, with various deviations where I saw fit, to better match .NET/C# implementation details. Things like UtlVector/UtlMap/UtlLinkedList are replaced with their C# equivalents. Each "stage" described here builds on each other incrementally - ie. stage 3 can include stage 2 and stage 1 libraries, stage 2 can include stage 1 libraries, but stage 1 cannot include stage 2 libraries, etc. Libraries can include other libraries within their own stage if needed - but is done only in a few cases (VTF including Common and Bitmap, for example.)

There are currently seven stages. The Solution is organized via Solution Folders as well in this order.

Note

AppFramework in Source is instead replaced by Microsoft.Extensions.DependencyInjection.


Stage 0: External Dependencies + Global Usings

Various external dependencies & components, along with various non-project-specific C# files, which allow defining global using statements for a project. For example, almost every library uses this in its .csproj file to add the various type definitions/global static methods/constants:

<ItemGroup>
	<Compile Include="..\Usings\GlobalUsings.cs" Link="Usings/GlobalUsings.cs" />
</ItemGroup>

Stage 1: Engine Common + Tier Libraries

The Source SDK provides a "public" folder which contains the vast majority of engine interfaces, enumerations, etc. These are statically linked at compile time. There are also dynamic libraries (tier0) and static libraries (tier1, tier2) which various components use shared functionality from. In Source.NET, these are all merged into one single common dynamic library, since C# doesn't support static linking.

Note

A future goal is to further separate these files - tier0 and tier1 being their own individual projects, for example, rather than tier0 and tier1 functionality being contained within the Source.Common dll.

Note

This stage also includes a couple other libraries, such as bitmap/common - which also are static libraries in C++, but in our case, are dynamic libraries.

Libraries

  • Bitmap
    • Bitmap processing
    • Image format utilities
  • Common
    • Public interfaces and enumerations
    • Various helper components
    • Tier0 functionality - Dbg, Spew, Asserts, etc
    • Tier1 functionality - ConVars, implementation of ICVar happens here instead of a vstdlib equiv
  • VTF
    • VTF texture parsing
    • Implementation of IVTFTexture
  • VPK

Stage 2: Pre-Engine Components

These are components of the engine which the engine includes directly. This is a rare case as most things are implemented in the post-engine stage, but things like VGUI and VGUI controls have to be in this stage.

Libraries

  • GUI
    • Defines Panel : IPanel, which is the root of all elements
    • Implementation of IVGui
    • Implementation of IVGuiInput
    • Implementation of ISchemeManager
    • Implementation of ILocalize
  • GUI.Controls
    • Defines the various GUI controls
    • Also defines AnimationControllers, BuildGroups, NavGroups, etc.

Stage 3: Engine Main

This is only the engine core. Everything else beyond this point are extensions which are included by the launcher/dedicated projects.

Libraries

  • Engine
    • Core engine logic
    • Cbuf, Cmd, Key, ModelLoader, Render, Scr, Sound, Sys, View, etc.
    • Implementation of ICommandLine
    • Datatable encoding definitions
    • Implementation of IEngineAPI and EngineBuilder to build one

Stage 4: Post-Engine Components

These are the implementations of various systems the engine uses. A lot of deviations happen in these components.

Libraries

  • FileSystem
    • Implements IFileSystem
    • Handles the various types of search paths
      • Folder/disk based
      • .vpk v1/v2 files
      • .bsp pakfile lumps
  • MaterialSystem
    • Implements IMaterialSystem & IMatRenderContext
    • Implements IShaderAPI
    • Implements IShaderDevice
    • Implements IShaderUtil
    • Implements ITextureManager
    • Implements IShaderSystem
    • Implements ISurface/IMatSystemSurface
  • InputSystem
    • Implements IInputSystem around SDL 3
    • Produces input events from the game window (wrapped around an IWindow interface, which ILauncherMgr produces)
  • LauncherManager
    • Implements ILauncherMgr around SDL 3
    • Produces the game window
  • System
    • Implements ISystem around SDL 3
  • StdShader.Gl46
    • Defines BaseShader, BaseVSShader
    • Implements IShaderDLL (StdShaderGl46)
    • The core logic for various shaders (currently only UnlitGeneric for now)

Warning

MaterialSystem/ShaderAPI/StdShader etc. deviates heavily from Source and generally is an abomination of horribleness. DirectX 8/9 is a very outdated API + graphics programming is complicated + whatever the hell Valve was doing I only barely was able to understand. If anyone wishes to help clean up the graphics API, that would be very appreciated... it took me nearly 3 weeks to get to this point :^(


Stage 5: Game Realms

This is the actual game code. The closest thing to a static library in this entire repository is the Shared project. It is explicitly set to not compile as a library, and is instead included as a folder in both the Client and Server libraries. The client DLL defines CLIENT_DLL and the server DLL defines GAME_DLL. This allows the shared code to use shared logic with deviations in client/server, pretty much exactly how Source does it.

Libraries

  • Game.Client
    • Implements IBaseClientDLL
    • Core client logic
  • Game.Server
    • Implements IServerGameDLL
    • Core server logic
  • Game.Shared
    • Directly compiled as code in Client and Server
    • CLIENT_DLL and GAME_DLL constants can be used to change how Shared is built
  • Game.UI
    • Implements IGameUI
    • Main menu logic

Stage 6: Executables

The Launcher/Dedicated executables. For now, no dedicated project is being worked on - but if it ever is, it would be made here.

Executables

  • Launcher
    • Defines the IBaseClientDLL, IServerGameDLL, IGameUI implementations from stage 5
    • Builds the engine service collection

API Notes

Important

This was just a rough list of things I wanted to write down. I probably will document things better later. This just goes into some details about the app structure

Adding Components

There are three ways to do this:

The Common Way: Engine Builders<T>

Important

Dedicated does not exist yet, but it would fundamentally do the same thing.

In Launcher/Dedicated projects, the engine is built up with an EngineBuilder instance, with individual calls to WithComponent to specify which components the game project needs.

WithAssembly(string assemblyName)

This pre-loads an assembly by name. This is only needed for projects with no references in-code when the engine is built - because otherwise, ConVar resolving, SourceDllMain resolving, etc. will miss those assemblies entirely.

WithComponent<T> or WithComponent<Interface, Implementation>

This allows you to specify a component by its explicit type or implementation of an interface. For example; this is how IFileSystem is defined in Launcher:

.WithComponent<IFileSystem, BaseFileSystem>()

... and here's how SDL LauncherManager is defined:

.WithComponent<SDL3_LauncherManager>()
.WithResolvedComponent<ILauncherManager, SDL3_LauncherManager>(x => x.GetRequiredService<SDL3_LauncherManager>())
.WithResolvedComponent<IGraphicsProvider, SDL3_LauncherManager>(x => x.GetRequiredService<SDL3_LauncherManager>())

Important

WithComponent<> and its various extensions defined later search for a static "DLLInit" method in the concrete type. If that method is found, it is cast to a delegate void(IServiceCollection) and executed on the spot. This allows components to define their own individual includes/usings when included as a dependency. An example can be found in HLClient, where it uses DLLInit to include the input system and a few other things.

WithResolvedComponent<Interface, Implementation>

WithResolvedComponent allows resolving an interface type to a concrete type. It is only used in some cases (like the above).

WithGameDLL<T> where T : IServerGameDLL

WithClientDLL<T> where T : IBaseClientDLL

WithGameUIDLL<T> where T : IGameUI

WithStdShader<T> where T : IShaderDLL

These are all used to include their specific implementations.


Public Static Class Named "SourceDllMain"

All assemblies are searched for a static class named "SourceDllMain". If this class is found, a method named "Link" is searched for, and cast to a delegate void (IServiceCollection). This is searched for and ran after EngineBuilder.Build() inserts its own engine components. This can be helpful if an assembly has its own specific components it wishes to include.

Note

A few components also use this as a way to perform some static actions. SDLManager for example uses this to define assert dialog logic.

Public Class with [EngineComponent] Attribute

All assemblies are searched for classes with the EngineComponentAttribute. All classes with this attribute are added as concrete singletons into the service collection after EngineBuilder.Build() inserts its own engine components.

Future Plans

I intend to do physics simulation with BepuPhysics v2. I believe that this will serve as a viable replacement - after trying out VPhysics-Jolt by misyltoad as purely a clientside module (because of this issue causing clientside crashes), I noticed a significant increase in clientside performance while still seeing relative stability (a few things were broken, but it wasn't that bad). This makes me believe that any general physics engine could be used in place of VPhysics on the clientside for the sake of prediction, but we'll have to see in testing. It definitely would be less of a nightmare to do this than trying to somehow use IVP from managed code. If singleplayer is ever implemented, it would just be using this. The interfaces will be pretty similar to VPhysics's interfaces.

About

An open-source Source Engine replica, written in C#, capable of joining real Garry's Mod servers to some extent (very much so an alpha right now)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 5