#This file was created by Mon May 3 14:49:36 1999 #LyX 1.0 (C) 1995-1999 Matthias Ettrich and the LyX Team \lyxformat 2.15 \textclass linuxdoc \language default \inputencoding default \fontscheme default \graphics default \paperfontsize 10 \spacing single \papersize Default \paperpackage a4 \use_geometry 0 \use_amsmath 0 \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \quotes_times 2 \papercolumns 1 \papersides 1 \paperpagestyle default \layout Title \added_space_top vfill \added_space_bottom vfill Clone design \layout Author Fabrice Rossi \layout Abstract This document tries do describe the design of the clone game. \layout Standard \begin_inset LatexCommand \tableofcontents{} \end_inset \layout Section Sound \layout Subsection Design \layout Subsubsection A sound server thread \layout Standard Clone sound handling must be configurable, as the whole clone game tries to be. It has also some specific problems. Sound playing involve IOs which are quite asynchronous from the rest of the game. This basically means that sound playing and the rest of the game should be handled in parallel. The classical solution is to use a sound server implemented in an independant process. For efficiency reasons, communication between the sound process and the game process is made through shared memory. \layout Standard As a thread fanatic, I've chosen a threaded solution, based on posix threads. The main advantage is the ease of programmation: memory is always shared between treads. Another important point is efficiency: context switching between threads is much more efficient than between processes. Moreover, synchronization between threads can be more efficient than between processes (thanks to LinuxThread). \layout Standard Except direct consequenses of the thread choice, the solution is quite classical : clone game engine send orders to the sound server thread, which in turns sends sound data to the hardware (well, to the sound driver...). \layout Subsubsection Communication \layout Standard Communication between the game engine and the thread server is entirelly based on shared memory. The main communication \begin_inset Quotes eld \end_inset device \begin_inset Quotes erd \end_inset is a FIFO. Each time the game engine wants a sound to be played, it puts an order in the FIFO and increments a semaphore. The sound server thread is in general sleeping on the semaphore. When it wakes up, it gets orders from the FIFO and turns them into sample playing orders. Each time a sound has been completely played, the semaphore is decremented: the server thread will fall asleep when the FIFO is empty again. \layout Standard Thanks to shared memory, the sound server thread is used only to actually play sounds (this is quite different from the classic \begin_inset Quotes eld \end_inset sound server in a process \begin_inset Quotes erd \end_inset solution). The communication FIFO is not designed to handle anything different from sound playing. The global sound volume for instance is handle through a global variable. This variable is used by the server thread to compute the data sent to the sound device driver. The game engine can concurrently modify it. \layout Standard The main advantage of this design is its simplicity: the sound server thread is only used to answer to the asynchronous IO problem. It is not a distributed approach and therefore the communication between the game engine and the sound layer remains very simple: you don't have volume command, sound loading command, etc. \layout Subsubsection Sound identification \layout Standard Each sound is identified on both side (client and server) with an unique id. On client side, this id is associated to a name, which allows easy manipulation through C or other languages (for instance the embended guile interpreter). This association is completely done on the client side. \layout Standard On the server side, a sound id is associated to several samples. A simple sound uses only one sample, but we have also sound groups. When asked to play a sound id associated to a sample group, the server will randomly choose a sample. This allows to implement some warcraft like features completely on server side. The sound server can also use more complex schemes to choose a particular sample (in order to implement warcraft like selection sounds). \layout Standard Sound loading is done by the game engine thread and makes of course use of shared memory. The clone engine can load dynamically a new sound and then ask the server to play it (for instance through the guile prompt). The normal way to load a sound is to use the client side, which will store the mapping between the sound name and the id produced by the server side (beware that the server side code can be runned either by the game engine thread or by the sound server thread). But it is also possible (and not a good idea!) to load directly a sound using server side functions. \layout Subsubsection Other features \begin_inset LatexCommand \label{other:features} \end_inset \layout Standard The sound layer has been designed to handle the following features: \layout Itemize sound volume: each sound playing request can have its own volume (computed by the game engine). \layout Itemize distance influence: rather than giving the volume, the game engine can give to the sound server a distance between the origin of a sound and the current view point. The sound server is able to compute a volume that takes into account sound characteristics (each sound can have a range) and global parameters (there is a global maximal range as well as a global volume). \layout Itemize noise reduction: each sound request has a producer and the sound server can discard a request if its producer (e.g. an unit) is already playing something. \layout Subsubsection Organization \layout Standard The sound layer is currently organized with the help of four files: \layout Itemize \family typewriter sound_id.[h/c] \family default : contains everything related to the client side manipulation of sound ids (client side loading, mapping between a name and a sound id, etc.). \layout Itemize \family typewriter sound.[h/c] \family default : contains everything related to client side sound playing (sound order sending and volume manipulation). \layout Itemize \family typewriter sound_server.[h/c] \family default : contains everything related to server side sound (sound order fifo, server side sound loading, etc.). \layout Itemize \family typewriter ccl_sound.[h/c] \family default : contains the glue between the C sound manipulation and the guile sound manipulation. \layout Subsection Server side implementation ( \family typewriter sound_server.c \family default ) \layout Subsubsection Low level part \layout Standard This part part of the sound layer is responsible of low level operations: \layout Itemize \series bold sound file loading \series default : currently only the wav file format is supported ( \family typewriter LoadWav \family default ). The loading function must procude a \family typewriter Sample \family default pointer. The \family typewriter Sample \family default structure contains information about the sound (stereo or mono, frequency, resolution, length) as well as a pointer to the raw data. \layout Itemize \series bold sound mixing \series default : for portability (and code simplicity) we are only using here \family typewriter /dev/dsp \family default and we are therefore doing hand mixing. This is done thanks a channel structure. We have \family typewriter MaxChannels \family default channels. Each channel can be used to play a sample: it contains a pointer to a \family typewriter Sample \family default , some information about the source of the sound, the volume, etc. Channels are mixed thanks to \family typewriter MixChannelsToStereo32 \family default (and \family typewriter ClipMixToStereo8 \family default ). Real mixing is done in \family typewriter MixSampleToStereo32 \family default . This function takes into account the volume associated to each channel. \layout Itemize \series bold sound device initialization \series default : the \family typewriter InitSound \family default function is responsible of preparing the sound layer. Its tasks include the sound device initialization. \layout Subsubsection Sound id handling \layout Standard On server side, sound are identified by ids which are considered as having the type \family typewriter SoundServerId \family default . A \family typewriter SoundServerId \family default is simply a pointer to a \family typewriter Sound \family default structure, which is the server side representation of a sound. We have basically three types of sounds: \layout Enumerate \series bold Simple sounds \series default : a simple sound is identified by the value \family typewriter ONE_SOUND \family default in the \family typewriter Number \family default member of its \family typewriter Sound \family default structure. It consists of single sample. When asked to play a simple sound, the sound server will put the corresponding sample in a free channel. \layout Enumerate \series bold Sound groups \series default : a sound group is identified by having a value greater than one in the \family typewriter Number \family default member of its \family typewriter Sound \family default structure. A sound group consists of \family typewriter Number \family default sound samples. When asked to play a sound group sound, the sound server will put a randomly chosen sample (among the possible ones) in a free channel. \layout Enumerate \series bold Sound \begin_inset Quotes eld \end_inset two groups \begin_inset Quotes erd \end_inset \series default : a \begin_inset Quotes eld \end_inset two groups \begin_inset Quotes erd \end_inset sound is identified by the value \family typewriter TWO_GROUPS \family default in the \family typewriter Number \family default member of its \family typewriter Sound \family default structure. This is a specialy designed sound used to implement Warcraft like selection sound. A two groups sound contains in fact two sound ids, which should in general correspond to two sound groups. The way the sound server handles a \begin_inset Quotes eld \end_inset two groups \begin_inset Quotes erd \end_inset playing request is quite complex and will be described in the section \begin_inset LatexCommand \ref[about sample selection]{sample:selection} \end_inset . \layout Standard Server side handling of sound id includes of course manipulation functions: \layout Itemize \series bold sound registering \series default : the server side has two functions that allow sound id production. \family typewriter RegisterSound \family default is used to ask the sound server to register (and load currently) simple sounds or sound groups. It takes a list of sound files and produces a sound id. \family typewriter RegisterTwoGroups \family default is used to tell the sound server to build a two groups sound with two sound ids. \layout Itemize \series bold sample selection \series default : the server side is of course able to map a sound id to a raw sample. As described before, the mapping depends on the sound type and is based on two functions: \family typewriter ChooseSample \family default and \family typewriter SimpleChooseSample \family default . \layout Itemize \series bold range manipulation \series default : the server provides a function (SetSoundRange) that allows to change the range of a given sound. \layout Subsubsection Source registration \layout Standard As explained in the section \begin_inset LatexCommand \ref[about other features]{other:features} \end_inset , the sound server has been designed to be able to discard sounds. If you keep on clicking on an unit, clone won't play hundreds of acknowledgment : the unit is basically non receptive until is has finished to play its first acknowledgement. \layout Standard The server side defines a \family typewriter Origin \family default structure that can be used to specify where a sound comes from (each sound request can provide an origin in the FIFO). This structure is used for two purposes: \layout Enumerate \series bold for noise reduction \series default : when the sound thread handles a request, it first look if it should keep it (thanks to \family typewriter KeepRequest \family default ). A request with no origin is currently always kept (but the system is already designed to be more subtle), whereas a request with an origin is kept if the same origin is not currently already playing something. The sound layer register each origin in a hash table ( \family typewriter UnitToChannel \family default ) that maps an origin to a channel: if the origin is already in the hash table, then the sound server discards the sound request. If not, it registers the origin in the hash table (with \family typewriter RegisterSource \family default ) and handles normally the request. When the channel has been completely played by the low level part, the origin is removed from the hash table (thanks to \family typewriter UnRegisterSource \family default ). \layout Enumerate \series bold for two groups sound handling \series default : some sounds are considered as selection sounds. When it is the case, the origin of sound is kept in a special structure named \family typewriter SelectionHandler \family default . This structure is used in \family typewriter ChooseSample \family default to implement the Warcraft selection sound system. \layout Subsubsection Sample selection \begin_inset LatexCommand \label{sample:selection} \end_inset \layout Standard A two groups sound is handled through a \begin_inset Quotes eld \end_inset complex \begin_inset Quotes erd \end_inset mechanism: \layout Enumerate when a sound is flagged as a selection sound (this is one of the possible properties of a sound playing request), its origin is kept in the \family typewriter SelectionHandler \family default structure. \layout Enumerate When \family typewriter ChooseSample \family default is asked to choose a sample for a two groups sound, it proceeds as follows: \begin_deeper \layout Enumerate it compares the sound origin with the one stored in \family typewriter SelectionHandler.Source \family default . \layout Enumerate if origins are different: \begin_deeper \layout Enumerate it registers the current origin in \family typewriter SelectionHandler \layout Enumerate it fixes \family typewriter SelectionHandler.HowMany \family default to 1 \layout Enumerate it fixes \family typewriter SelectionHandler.Sound \family default to the \emph on first \emph default sound of the two groups \layout Enumerate it selects \series bold randomly \series default a sample from the \emph on first \emph default sound group of the two groups \end_deeper \layout Enumerate if origins are equals: \begin_deeper \layout Enumerate it compares \family typewriter SelectionHandler.Sound \family default to the \emph on first \emph default sound of the two groups: \layout Enumerate if they are equals: \begin_deeper \layout Enumerate it selects \series bold randomly \series default a sample from the \emph on first \emph default group and increments \family typewriter SelectionHandler.HowMany \family default by one. \layout Enumerate If this value reaches 3, the sound server sets it to zero and sets \family typewriter SelectionHandler.Sound \family default to the \emph on second \emph default sound of the two groups. \end_deeper \layout Enumerate if they differ: \begin_deeper \layout Enumerate it selects the sample \series bold number \series default \family typewriter SelectionHandler.HowMany \family default from the second sound group. \layout Enumerate it increments \family typewriter SelectionHandler.HowMany \family default . \layout Enumerate if there are no more samples in the group, the sound server sets \family typewriter SelectionHandler.Sound \family default to the \emph on first \emph default sound of the two groups (and sets \family typewriter SelectionHandler.HowMany \family default to 0). \end_deeper \end_deeper \end_deeper \layout Standard The consequences of this algorithm are quite simple: when you keep on selecting the same unit, it first randomly choose a sample from its first group (this is the \emph on selection \emph default sound in Warcraft). It does this three times, then it switches to the second group of samples. For this group, the sound server cycles through the samples (this is the \emph on annoyed \emph default sound in Warcraft). When the cycling is done, the sound server reverts to the first group. If any selection sound (it does not have to be a two groups sound) is played during these operations, everything is reset to the beginning. \layout Subsubsection Distance to volume mapping \layout Subsubsection Command FIFO \layout Standard Communication between the game engine and the sound server thread is done thanks to a FIFO. The FIFO is currenty implemented thanks to a fixed size array (its size is \family typewriter MAX_SOUND_REQUESTS \family default ). Each element of the array is a \family typewriter SoundRequest \family default structure. The structure has the following members: \layout Description Source this is the origin of the sound \layout Description Power this is either the distance of the sound origin to the current view point or the requested sound volume \layout Description Sound this is the sound id of the sound to play \layout Description Used 1 if this FIFO slot is used \layout Description Fight 1 if the sound is a fight sound (currently not used) \layout Description Selection 1 if the sound should be considered as a selection sound \layout Description IsVolume 1 if Power is the requested volume, 0 if it is a distance \layout Standard Currently, the sound server code does not offer any support for the Command FIFO. A low level call is included in the client side code and should perhaps been moved here. \layout Subsubsection Command processing \layout Standard Commands waiting in the FIFO are processed in the \family typewriter FillChannels \family default function. For each request, this function checks wether the request should be kept. If it is the case, it registers the source of the request, chooses a sample and put it into a free channel. This function does not decrement the sound thread semaphore ( \family typewriter SoundThreadChannelSemaphore \family default ) because this must be done \emph on after \emph default the sound is played. \layout Subsubsection Server thread main loop \layout Standard The server thread implements the following infinite loop (in \family typewriter WriteSoundThreaded \family default ): \layout Enumerate call to \family typewriter FillChannels \family default to transform sound requests into samples (put in channels). This is the high level part of the main loop. \layout Enumerate process channels, thanks to \family typewriter MixChannelsToStereo32 \family default , \family typewriter ClipMixToStereo8 \family default , and other low level operations. \layout Enumerate decrement the sound thread semaphore ( \family typewriter SoundThreadChannelSemaphore \family default ) for completely processed channels. When the last occupied channel is processed, the sound server thread start sleeping on the semaphore (this is a side effect of decreasing it). \layout Subsubsection Support for non threaded sound \layout Standard The sound server code contains a function called \family typewriter WriteSound \family default that can be used to implement sound playing without a sound thread (this is not a good idea on slow computers!). Clone can be compiled without any thread support (you just have to remove the definition of \family typewriter USE_THREAD \family default in the makefile). A more flexible approach uses guile configuration: you can specify at starting time if you want to use or not a threaded sound (default: not). \layout Standard If you don't use a threaded sound, clone main loop will call at regular time \family typewriter WriteSound \family default . This function does one loop similar to the server thread main loop. \layout Subsection Sound Id manipulation (client side, \family typewriter sound_id.c \family default ) \layout Subsubsection Sound names \layout Standard On the client side, clone implements a way to associate a sound name (a string) to a sound id. This is done thanks to a hash table ( \family typewriter SoundNameToId \family default ). Several functions make use of it: \layout Itemize \family typewriter SoundIdForName \family default : this function gives the sound id corresponding to a sound name. \layout Itemize \family typewriter MapSound \family default : this function adds a new mapping between a sound name and an already existing sound id. \layout Subsubsection Sound creation \layout Standard The client side has some wrappers for server side sound manipulation: \layout Itemize \family typewriter MakeSound \family default : calls \family typewriter RegisterSound \family default in the server side to obtain a sound id and store the mapping between the sound name and the id in the hash table. \layout Itemize \family typewriter MakeSoundGroup \family default : calls \family typewriter RegisterTwoGroups \family default in the server side to obtain a sound id and store the mapping between the sound name and the id in the hash table. \layout Subsection Sound orders (client side, \family typewriter sound.c \family default ) \layout Standard The client side has some wrappers to insert sound commands in the server FIFO. \layout Subsubsection Unit and missile sounds \layout Standard Most of clone sounds are played as a side effect of an unit action. Each unit action that can be associated to a sound is described by an enumerate of type \family typewriter UnitVoiceGroup \family default . This allows simpler manipulation in the client side. \layout Subsection Guile binding ( \family typewriter ccl_sound.c \family default ) \layout Subsection Future plans \the_end