Copyprotect Watermark

From Valve Developer Community
Jump to navigation Jump to search

Implementing a watermark for Source Engine mods can be useful in protecting against plagiarism of the binary (.dll) files.

The finished watermark, replace the title with your mod's title and the text below with your website.

This watermark comes from PmbrugGaming's (my) mod called Gabe Mod, and can be applicable to any mod with minimal changes.

Note: This perfectly works on mods running under Source 2007.

Some of the code was assisted by artificial intelligence, specifically ChatGPT 5.2. Human changes and fixes of the code were made.

To Start

  1. Create a mod in any of the source engine branches (I will be using Source 2007 in this tutorial)
  2. Open the mod's source code in the respective Visual Studio version
  3. Create a file in the client-side project titled: (your mod's name)_watermark.cpp

Includes and Pre-Class definitions

#include "cbase.h"
#include "hud.h"
#include "hudelement.h"
#include "hud_macros.h"
#include "iclientmode.h"
#include "vgui/IScheme.h"
#include "vgui/ISurface.h"
#include "vgui/ILocalize.h"
#include "vgui_controls/Panel.h"

// Memdbgon.h MUST be the last include in a .cpp file!!!!!
#include "tier0/memdbgon.h"

using namespace vgui;

extern IClientMode *g_pClientMode;

#define FONTFLAG_ANTIALIAS 0x010

ConVar watermark_r( "watermark_r", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Watermark color Red." );
ConVar watermark_g( "watermark_g", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Watermark color Green." );
ConVar watermark_b( "watermark_b", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Watermark color Blue." );
ConVar watermark_a( "watermark_a", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Watermark alpha." );

// ----------------------------------------------------------------------

These are the includes that are required to successfully compile and render the watermark. The watermark will be based on the HUD.

Below is the namespace so that we don't need to declare it again. ex: vgui::surface-> (more work); surface-> (easier). We grab the external class members from IClientMode via a pointer. Technically you don't need to define FONTFLAG_ANTIALIAS, but the compiler couldn't find the location so I just defined it there. Controls to style up the watermark (optional with a few removals)

You can also put a prefix behind the convars if wanted. ex: mymod_watermark_r

Class

class CHudWatermark : public CHudElement, public Panel
{
public:
    DECLARE_CLASS_SIMPLE( CHudWatermark, Panel );

    CHudWatermark( const char *pElementName );

    virtual void ApplySchemeSettings( IScheme *pScheme );
    virtual bool ShouldDraw( void );
    virtual void Paint( void );

private:
    HFont m_hFontTitle;
    HFont m_hFontWebsite;
};

DECLARE_HUDELEMENT( CHudWatermark );

There really isn't much to the class. It only contains a few functions and some private members. The functions tell the hud element to apply scheme settings, if it should draw, and what to do when it renders (paints). The members are for the handling of fonts. DECLARE_HUDELEMENT registers the element and notifies the game of its presence.

Functions Prior to Paint()

CHudWatermark::CHudWatermark( const char *pElementName ) 
    : CHudElement( pElementName ), Panel( NULL, "HudWatermark" )
{
    SetParent( g_pClientMode->GetViewport() );
    SetHiddenBits( 0 );

    int w, h;
    surface()->GetScreenSize( w, h );
    SetPos( 0, 0 );
    SetSize( w, h );
}

void CHudWatermark::ApplySchemeSettings( IScheme *pScheme )
{
    BaseClass::ApplySchemeSettings( pScheme );

    m_hFontTitle = pScheme->GetFont( "Tahoma", true );
    if ( !m_hFontTitle )
    {
        m_hFontTitle = surface()->CreateFont();
        surface()->SetFontGlyphSet( m_hFontTitle, "Tahoma", 24, 700, 0, 0, FONTFLAG_ANTIALIAS );
    }

    m_hFontWebsite = pScheme->GetFont( "Verdana", true );
    if ( !m_hFontWebsite )
    {
        m_hFontWebsite = surface()->CreateFont();
        surface()->SetFontGlyphSet( m_hFontWebsite, "Verdana", 16, 500, 0, 0, FONTFLAG_ANTIALIAS );
    }

    int w, h;
    surface()->GetScreenSize( w, h );
    SetPos( 0, 0 );
    SetSize( w, h );
}

bool CHudWatermark::ShouldDraw( void )
{
    return true;
}

The constructor sets the element to its initial settings (defaults with no value).

ApplySchemeSettings tells both texts what font and font settings they should use. It also does the same thing as the constructor at the end of the function.

ShouldDraw tells the game to draw the element all the time unless cl_drawhud is set to 0 (Plagiarizers may attempt to use this, but people will eventually turn it back to 1!).

Paint(): Where the rendering actually happens

Now that we've registered the hud element and got the previous functions, let us begin with the Paint() function, the heart of what you actually see in the hud.

void CHudWatermark::Paint( void )
{
    int screenW, screenH;
    surface()->GetScreenSize( screenW, screenH );

    wchar_t wszTitle[256];
    g_pVGuiLocalize->ConvertANSIToUnicode( "Your Mod Title Goes Here", wszTitle, sizeof( wszTitle ) ); // IN Source 2006 and below, replace this with: wchar_t wszTitle[] = L"Your Mod Title Goes Here";

    wchar_t wszWebsite[256] = L"";
    V_wcsncpy(
        wszWebsite,
        L"Your Website URL Goes Here",
        sizeof( wszWebsite )
    );

    int r = watermark_r.GetInt();
    int g = watermark_g.GetInt();
    int b = watermark_b.GetInt();
    int a = watermark_a.GetInt();

    int titleW, titleH;
    surface()->GetTextSize( m_hFontTitle, wszTitle, titleW, titleH );

    int websiteW = 0, websiteH = 0;
    if ( wcslen( wszWebsite ) > 0 )
        surface()->GetTextSize( m_hFontWebsite, wszWebsite, websiteW, websiteH );

    const int padding = 6;
    const int fadeSize = 8;

    int boxW = max( titleW, websiteW ) + padding * 2;
    int boxH = titleH + ( websiteH ? websiteH + 2 : 0 ) + padding * 2;

    int x = screenW - boxW - 10;
    int y = 10;

    // --------------------------------------------------
    // Fade-out edges
    // --------------------------------------------------
    for ( int i = fadeSize; i > 0; --i )
    {
        int alpha = ( a * i ) / ( fadeSize * 2 );

        surface()->DrawSetColor( 0, 0, 0, alpha );
        surface()->DrawFilledRect(
            x - i,
            y - i,
            x + boxW + i,
            y + boxH + i
        );
    }

    // --------------------------------------------------
    // Solid center box
    // --------------------------------------------------
    surface()->DrawSetColor( 0, 0, 0, a );
    surface()->DrawFilledRect(
        x,
        y,
        x + boxW,
        y + boxH
    );

    // --------------------------------------------------
    // Text Positions
    // --------------------------------------------------
    int titleX = x + boxW - padding - titleW;
    int titleY = y + padding;

    int websiteX  = x + boxW - padding - websiteW;
    int websiteY  = titleY + titleH + 2;

    // --------------------------------------------------
    // Title
    // --------------------------------------------------
    surface()->DrawSetTextFont( m_hFontTitle );
    surface()->DrawSetTextColor( r, g, b, a );
    surface()->DrawSetTextPos( titleX, titleY );
    surface()->DrawPrintText( wszTitle, wcslen( wszTitle ) );

    // --------------------------------------------------
    // Website
    // --------------------------------------------------
    if ( wcslen( wszWebsite ) > 0 )
    {
        surface()->DrawSetTextFont( m_hFontWebsite );
        surface()->DrawSetTextColor( r, g, b, a );
        surface()->DrawSetTextPos( websiteX, websiteY );
		surface()->DrawPrintText( wszWebsite, wcslen( wszWebsite ) );
    }
}

After all of the code is put in, compile the source code.

HudLayout.res

Put this in your mod's HudLayout.res:

	HudWatermark
    {
		"fieldName"	"HudWatermark"
		"xpos"		"0"
		"ypos"		"0"
		"wide"		"640"
		"tall"		"480"
		"visible"	"1"
		"enabled"	"1"
    }

Conclusion

And that is all of it! This watermark includes:

  • Title space for your mod, positioned on the right
  • Website URL space below the mod name
  • A fade out near the edges to look classy
  • Color convars for the player to personalize the watermark.

If everything was put together correctly, the watermark should look like the picture at the top, with your mod name and website of course. Feel free to make any code changes to your watermark as wanted! This shall be open-source.