Gopher


the demo player


1 - How does it work?

Gopher works a bit like a conventional music tracker: it "plays" a file row by row except instead of mixing samples it calls functions with interpolated parameters corresponding to the current row.

2 - Why do I care?

You can write your effects once and then [re-]use them in different parts of your demo, in different demos or share them with other sceners.

Another advantage is that you'll never have to write synchronization code again. I simply got sick of breaking up my demo in a whole bunch of small "while" loops and hardcoding the sequence and parameters of my effects.

Portability: the player is written using SDL so your demo should be portable everywhere SDL is supported. Portability also means if a member of your demogroup only uses Linux and your other coders use Windows, she'll still be able to contribute.

Lastly: you'll never have to recompile anything to change how your demo behaves, what texture it should use, when some effect starts and eventually, a demo editor that looks and acts somewhat like Adobe Premiere can be written to make demo editing a visual process with realtime previews and a tracker-like interface.

3 - How do I use it?

This assumes your development computer is configured to use SDL and that you already know how to create DLLs and to operate a C or C++ compiler.

  1. Create a Gopher-compatible library with all your effects in it (Section 4)
    1. Create a DLL
    2. Create and export Gopher functions
    3. Create and export your effects
    4. Make your library externally configurable
  2. Create data your DLL needs (Section 5)
  3. Create a Gopher Demo File (Section 6)
    1. Create a Library section
    2. Create a Demo section
    3. Create a Summary section
    4. Create a Settings tag
    5. Create Track sections

4 - Creating a Gopher-compatible library

4.1 - Create a DLL

  1. with your favorite C/C++ development tools, create a DLL
  2. have your DLL link to sdl.lib
  3. make sure the debug version uses the "Debug Multithreaded DLL" version of the run-time library and the release version uses the "Multithreaded DLL" version (in VC6: Project/Settings/C++/Code Generation/Use run-time library)
  4. perform the necessary steps to have your compiler understand you wish to export functions by name, for run-time linking (in VC, this is done by creating a .DEF file with the same name as the main .cpp file and the resulting .dll file and adding a line that reads "EXPORTS" at the top. Add this file to your project.)

4.2 - Create and export Gopher functions

  1. In order to create a "Gopher plugin", you need to implement these 3 functions:
    BOOL Gopher_Init ( SDL_Surface ** outputSurfaces, SDL_Surface ** inputSurfaces );
    Gopher_Init is called once per demo by Gopher to give you a chance to create lookup tables, load data and otherwise prepare your effects. You get an array of outputSurfaces that you must keep a copy of because for every frame, one of them is going to be what gets put on the screen. You also get an array of inputSurfaces, which is the size you request in your Gopher Demo File and you can load whatever images you wish in there as this is the main mechanism you will use to pass the result of one effect on to the next. (except for pure OpenGL demos)
    BOOL Gopher_BeginNewFrame ( long currentOutputSurface );
    Gopher_BeginNewFrame will be called by Gopher once per frame to tell you some of your functions are about to be called and that if you need to write anything to the buffer that will be thrown on the screen, you should use outputSurfaces[currentOutputSurface]
    BOOL Gopher_Quit ( void );
    Gopher_Quit is the opposite of Gopher_Init and is called when the demo is over (either time has run out or the user hit ESC, Alt+F4, etc..) to give you a chance to uninitialize, close files, etc...
  2. perform the necessary steps to have your compiler export these three functions (in VC, this is done by appending the lines
    Gopher_Init
    Gopher_BeginNewFrame
    Gopher_Quit
    ...to the .DEF file you created in section 4.1)

4.3 - Create and export your effects

  1. unless your library simply loads images at Gopher_Init (like Mystic), you will want to create some effects that Gopher will execute
  2. your effects must be contained to single functions that will take as input parameters what input surface(s) to use (if any), what values to use to drive the effects themselves and what surface(s) you would like to output to (either the screen backbuffer itself, or one of the "input" surfaces so that the output of one effect can be the input of the next)
  3. your functions have to return a BOOL and all their parameters must be "signed long"
  4. perform the necessary steps to have your compiler export all the functions you would like to use in a demo (in VC, this is done by appending the name of the function
    Plasma
    ...to the .DEF file you created in section 4.1.

For example:

BOOL Plasma ( signed long wave1, signed long wave2, signed long wave3, signed long outputSurfaceIndex );
...this function takes three parameters that determine what the effect looks like (wave1-3) and one parameter that determines where the effect is rendered (outputSurfaceIndex)

I use the convention that if one of my effects receives '-1' as outputSurfaceIndex, the effect is rendered to one of the outputSurfaces (specifically, outputSurfaces[currentOutputSurface]) otherwise, the effect is rendered in inputSurfaces[outputSurfaceIndex]

This convention isn't necessary if you would rather have one of your functions take an index into inputSurfaces as a parameter and blit it to the current output surface, but this is an extra operation...

4.4 - Make your library externally configurable

Gopher is designed so that when it starts calling effect functions in your library, they execute very quickly and do exactly what they are told.

It may happen that a particular frame took longer to render than an another one, in this case that frame might have been the last one to call a specific effect function. This means you have no guarantee a given set of functions at a given millisecond in your track will ever get executed, because it is possible an execution prior to that took longer than expected (the system has to keep up: it was deemed more important to have it keep up and be synchronized than to produce perfect, fixed framerate output. This type of output could be achieved by an offline version of Gopher that outputs a video file or something..)

Effect functions are therefore not recommended to keep ANY state for future calls nor to expect to be called at all during a demo, in case some user cranks up the detail level on their computer and the demo runs at 1 frame per second.

Effect functions should have all their data initialized in Gopher_Init, which would preferentially read from an external file what special parameters should be set, so that changing what texture is loaded or what image maps are used is something that can be done without recompiling the library, hence extending its re-use and making it simpler for the person putting the demo together (especially if an editor is used in the future).

5 - Create data your DLL needs

Hopefully the suggestions in Section 4.4 were followed and your DLL is now externally configurable. Create some sample data so that you can test your code.

6 - Create a Gopher Demo File

Start a text file (ie: demo.xml) and insert the following text:

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher1.5.dtd">
<GopherDemoFile>
</GopherDemoFile>

6.1 - Create a Libraries section

  1. Insert the following lines between "GopherDemoFile" tags:
    <Libraries>
    </Libraries>
    
  2. Throw a section for each of your Gopher libraries between the "Libraries" tags:
    <Library FileName = "blarg.dll">
    </Library>
    
  3. If a given library does not expose any run-time functions, you can simply use an empty element and skip to section 6.2.
  4. Insert a "Function" section for every run-time function your Gopher library supports: (only insert the functions under their corresponding Library section!)
    <Function Name = "Plasma">
        <Parameter>
            wave1
        </Parameter>
        <Parameter>
            wave2
        </Parameter>
        <Parameter>
            wave3
        </Parameter>
        <Parameter>
            destSurface
        </Parameter>
    </Function>
    
  5. Your file should now look somewhat like this:
    <?xml version="1.0" encoding="windows-1252"?>
    <!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher1.5.dtd">
    <GopherDemoFile>
        <Libraries>
            <Library FileName = "blarg.dll">
                <Function Name = "Plasma">
                    <Parameter>
                        wave1
                    </Parameter>
                    <Parameter>
                        wave2
                    </Parameter>
                    <Parameter>
                        wave3
                    </Parameter>
                    <Parameter>
                        destSurface
                    </Parameter>
                </Function>
                <Function Name = "Blend">
                    <Parameter>
                        sourceSurface
                    </Parameter>
                    <Parameter>
                        blendAmount
                    </Parameter>
                    <Parameter>
                        destSurface
                    </Parameter>
                </Function>
            </Library>
            <Library FileName = "mystic.dll" />
        </Libraries>
    </GopherDemoFile>
    

6.2 - Create a Demo section

Insert the following lines after the "Libraries" element:

<Demo>
</Demo>

6.3 - Create a Summary section

  1. Insert the following lines inside the "Demo" element:
    <Summary>
    </Summary>
    
  2. Insert at least one "Author" tag inside the "Summary" element:
    <Author>
        Purple Motion
    </Author>
    
  3. The "Group" element is optional, but follows the "Author" element(s):
    <Group>
        Future Crew
    </Group>
    
  4. The "ExtraInfo" element is also optional, put after "Group" (or "Author" if "Group" isn't specified):
    <ExtraInfo>
        1st place at Assembly 1993
    </ExtraInfo>
    
  5. You should have something similar to this:
    <Demo>
        <Summary>
            <Author>
                Purple Motion
            </Author>
            <Author>
                Psi
            </Author>
            <Group>
                Future Crew
            </Group>
            <ExtraInfo>
                1st place at Assembly 1993
            </ExtraInfo>
        </Summary>
    </Demo>
    

6.4 - Create a Settings tag

Only one of these are necessary (and allowed) and it goes after "Summary", but before the end of "Demo":

    <Settings RunningTime = "56500"
            UseOpenGL = "false"
            NumberOfInputSurfaces = "32"
            InputMusicFile = "data\omf-ar0.s3m" />

6.5 - Create Track sections

For each Track you'll want to use (you need at least one) insert "Track" elements after the "Settings" element:

    <Track>
    </Track>

An important thing to know: for every frame, tracks are executed from last to first. This makes it easier to supply a "blit" function throughtout the demo as you only need to stick it in the first track and have one event execute throughout the demo as opposed to trying to use whatever track is free for a given event. (trust me on this one, I tried it the other way and it made the Gopher demo file very ugly and hard to use)

Just think of it like a music tracker and at the low level, channels are being played in reverse order, from right to left (ie: 4, 3, 2, 1).

A track is composed of one or more "Events". An Event is composed of fixed start and end time points, a function name, the library in which said function name is found, and a list of what the parameters should be equal to at the start of and the end of the event.

StartTime and EndTime are in milliseconds. Be careful not to overlap times for events within the same track. (times can overlap for events in different tracks)

<Event LibraryFileName = "blarg.dll" FunctionName = "Plasma" StartTime = "0" EndTime = "14000">
</Event>

Supply as many "param" elements, in the "Event" element, as the function needs parameters and give values to the "start" and "end" attributes:

    <param start="0" end="1000" />
    <param start="10000" end="0" />
    <param start="250" end="750" />
    <param start="31" end="31" />

In this example, the "Plasma" function in the library "blarg.dll" will be executed for every frame between 0 and 14000 miliseconds (14 seconds), with the first parameter going from 0 to 1000, the second from 1000 to 0, the third from 250 to 750 and the last staying put at 31 for the entire time.

Your completed demo file should look like this:

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher1.5.dtd">
<GopherDemoFile>
    <Libraries>
        <Library FileName = "blarg.dll">
            <Function Name = "Plasma">
                <Parameter>
                    wave1
                </Parameter>
                <Parameter>
                    wave2
                </Parameter>
                <Parameter>
                    wave3
                </Parameter>
                <Parameter>
                    destSurface
                </Parameter>
            </Function>
            <Function Name = "Blend">
                <Parameter>
                    sourceSurface
                </Parameter>
                <Parameter>
                    blendAmount
                </Parameter>
                <Parameter>
                    destSurface
                </Parameter>
            </Function>
        </Library>
        <Library FileName = "mystic.dll" />
    </Libraries>
    <Demo>
        <Summary>
            <Author>
                Purple Motion
            </Author>
            <Author>
                Psi
            </Author>
            <Group>
                Future Crew
            </Group>
            <ExtraInfo>
                1st place at Assembly 1993
            </ExtraInfo>
        </Summary>
        <Settings RunningTime = "56500"
                UseOpenGL = "false"
                NumberOfInputSurfaces = "32"
                InputMusicFile = "data\omf-ar0.s3m" />
        <Track>
            <Event LibraryFileName = "blarg.dll" FunctionName = "Plasma" StartTime = "0" EndTime = "14000">
                <param start="0" end="1000" />
                <param start="10000" end="0" />
                <param start="250" end="750" />
                <param start="31" end="31" />
            </Event>
        </Track>
    </Demo>
</GopherDemoFile>

That's it! Look at the supplied example (trix.xml) for what a simple, complete demo looks like and how files are typically organized for a demo.



Hosting generously provided by: SourceForge rocks the house!
Logo generously provided by: Dan Sauvé