BHS.DLL is a new dll created by Blackhand Studios to go alongside the custom scripts.dll and provide
gameplay fixes, new console commands and other enhancements. Basicly, all the stuff we are doing that
requires code to run client-side, sending data over the network or patches to the code.

BHS.DLL adds the following new console commands:
ID displays the name and ID of all players matching a specified string (e.g. ID jon matches jonwil and jonathan and abcjon but not abcjoe). Passing no string means "print all players".
PAMSG sends an admin message to a particular player
PPAGE sends a page to a particular player
SNDP plays a .wav sound (which can be from the always.dat of whichever mod is involved or one stored directly in the renegade data folder) for a specific player
SNDA is like SNDP but plays the sound for everyone
TEAM changes the team of a given player to the specified team. If they are alredy that team, nothing will happen
TEAM2 changes the team of a given player to the specified team. If they are alredy that team, nothing will happen. Unlike TEAM, it doesnt reset their score or cash.
DONATE takes a specified amount of cash from one player and gives it to another player
EXIT exits renegade
VERSION sends a message to the client of the specified player. The client then responds with a message indicating if bhs.dll is installed (and its version), this is then displayed on the console
SNDT is like SNDP but plays a sound for the specified team.
SND3DP and SND3DA like SNDP and SNDA except they are 3D. They take wav files.
TPAGE sends a page to a particular team
MLIMIT changes the mine limit (note that this will NOT be reset at the end of the map)
MUSICP plays a MP3 music file for a particular player
MUSICA plays a MP3 music file for all players
NOMUSICP stops the music for a particular player
NOMUSICA stops the music for all players
SVERSION prints the version of bhs.dll installed on the server
RADAR prints the current radar mode
MLIST prints the name of the map at a given index in the map list
MLISTC changes the name of the map at a given index in the map list
MAP prints the name of the current map
MOD prints the name of the current mod package
MAPNUM prints the index in the map list of the current map
MLIMITD prints the current mine limit
MINED prints the current mine count for a given team
EJECT ejects the specified player from whatever vehicle they are in (if any)
SONG prints the current song (a call to Set_Background_Music or use of MUSICA will change the song, a call to Stop_Background_Music or use of NOMUSICA will clear the song. The per-player commands have no effect on the current song.)
ICON displays an emoticon above the players head (like the radio commands only with no sound or text). You can make any w3d file appear. The emoticon is only visible to players on the team of the player whos head it appears over.
WIN ends the game in favor of a particular team by destroying all the buildings of the opposing team
SCREENSHOT changes the format of screenshots output by renegade. 0 = PNG, 1 = TGA. This setting gets saved into the registry. The default (if you have never used the SCREENSHOT command before) is PNG.
See below for details of the new PNG screenshot code
For reference, the setting is saved as ScreenshotFormat under the HKEY_LOCAL_MACHINE\SOFTWARE\Westwood\Renegade key, values are the same as for the console command (0 = png, 1 = tga)
SCREENFMT prints the current screenshot format
LOG changes whether the client chat log is output or not, 0 = disabled, 1 = enabled. This setting gets saved into the registry. The default (if you have never used the LOG command before) is enabled.
For reference, the setting is saved as ClientChatLog under the HKEY_LOCAL_MACHINE\SOFTWARE\Westwood\Renegade key, values are the same as for the console command (0 = disabled, 1 = enabled)
LOGP prints the status of whether the client chat log is being output or not
TMSG will send a team message as though it came from the specified player
GETBW will print the current bandwidth for a player (same thing as what the sbbo command sets)
SETBW will set the current bandwidth for a player (same thing as what the sbbo command sets)
Neither command will send anything to the client.
WOLSEND special command to send a packet required for LFDS WOL. Dont use this unless you know you need to send the packet. LFDS only.
PINFO new console command to print information about players in the game.
Data is comma delimited and is printed in the following order:
Player ID
Player Name
Player Score
Player Team
Player Ping
Player IP
Player KB/s
Player Rank (position in the player list)
Player Kills
Player Deaths
Player Money
Player Kill/Death Ratio
I wanted to add player time but I havent figured out how to retrieve that information yet

EXIT, LOGP, LOG, SCREENSHOT and SCREENFMT run client-side only.
ID runs both client-side and server-side.
everything else is server-side only.
The following commands require bhs.dll on the client, the rest do not:
SNDP
SNDA
SNDT
SND3DP
SND3DA
SND3DT
MUSICA
MUSICP
NOMUSICA
NOMUSICP
ICON

Also, BHS.DLL fixes the following script commands to work in multiplayer:
Set_Animation
Set_Animation_Frame
Enable_Stealth
Create_Explosion
Create_Explosion_At_Bone
Set_Fog_Enable
Set_Fog_Range
Set_War_Blitz
Fade_Background_Music
Set_Background_Music
Stop_Background_Music
Create_Sound
Create_2D_Sound
Create_2D_WAV_Sound
Create_3D_WAV_Sound_At_Bone
Create_3D_Sound_At_Bone
Play_Building_Announcement
Clear_Weapons (only for vehicles, infantry dont need it)
Enable_Vehicle_Transitions
Set_Player_Type
Set_Screen_Fade_Color
Set_Screen_Fade_Opacity
Set_Display_Color
Display_Text
Display_Float
Display_Int
Select_Weapon
Shake_Camera
Set_Obj_Radar_Blip_Shape
Set_Obj_Radar_Blip_Color

BHS.DLL also adds versions of the following script commands (named with _Player at the end and called directly without the Commands-> in front)
which take a GameObject and activate for the player represented by that object. Should that object not represent a player, nothing happens,.
Fade_Background_Music
Set_Background_Music
Stop_Background_Music
Enable_Radar
Display_GDI_Player_Terminal
Display_NOD_Player_Terminal
Set_Screen_Fade_Color
Set_Screen_Fade_Opacity
Force_Camera_Look
Enable_HUD
Create_Sound
Create_2D_Sound
Create_2D_WAV_Sound
Create_3D_WAV_Sound_At_Bone
Create_3D_Sound_At_Bone
Set_Display_Color
Display_Text
Display_Int
Display_Float

All of the commands except Create_Explosion and Create_Explsion_At_Bone require the client to have BHS.DLL (and scripts.dll) installed.
In some cases, not having it will render the map useless (e.g. they miss out on being able to access PTs because of the Display_Player_Terminal
commands or they dont get their radar disabled by Enable_Radar and gain an advantage) but in others (e.g. if Create_Sound is used to play sounds), it doesnt matter.
As mentioned below, use JFW_BHS_DLL if you want BHS.DLL to be required for the map.

Note that Set_Display_Color, Display_Text, Display_Float, Display_Int and the per-player versions of same are a little flakey and may not work right.

Also, BHS.DLL contains working Poke in multiplayer. This means that a script can respond to the Poked event and machines
other than the host can walk up to the object and use the action key on it. Like the script command fixes, this only works
if the client has BHS.DLL installed.
Also, BHS.DLL contains fixes (that require bhs.dll on the client) to make the Set_Model script command function correctly for vehicles and for infantry.
And it contains a fix so that giving a weapon powerup to a vehicle will function correctly. (which also needs bhs.dll on the client)

And, it has a fix that makes the harvester harvesting arms animation work correctly (this requires bhs.dll on the client for the animation fix)
Note that if you build a map that uses features such as Poke or Stealth or other "client is required" items
(i.e. if the client doesnt have bhs.dll installed, the map is unusable or not having it gives an advantage like with Enable_Stealth),
put the script JFW_BHS_DLL on an object in the map somewhere (something like a special Daves Arrow is good since its created on map startup).
This script prints a message to the console which regulators such as BrenBot can use to kick players without BHS.DLL installed.

BHS.DLL also features working radio command icons in multiplayer for all clients who have BHS.DLL installed.

BHS.DLL contains code to make renegade read different files instead of the *.dep/always.dep files.
This means that map loading is much faster.

BHS.DLL contains a new hook system that will be triggerd on the server when an object is created but before any observers on that object have run
To install an object create hook, you create an ObjectCreateHookStruct and fill it in with the address of the hook function plus a user-specified data pointer
(can point to whatever you like, it gets passed back to the hook function).
Then you pass the ObjectCreateHookStruct to AddObjectCreateHook.
To remove the hook, you pass the return value from AddObjectCreateHook to RemoveObjectCreateHook.
The hook will then trigger everytime an object is created.
Although there are a few instances where the Object Create Hook might not trip (e.g. certain spawner objects). This is because those objects dont
call through to ScriptableGameObj::Start_Observers (the place where the scripts get started up and the place I am hooking)

The hook function looks like this:
void ObjectCreateHook(void *data, GameObject *obj)
the value of data is what you passed in when you registered the hook.
the value of obj is the value for the newly created object.
Objects that are on the map at startup do not go through these hooks.
Look at JFW_Object_Create_Hook_Base and related scripts in jfwhook.h/jfwhook.cpp to see an example of how to use this.
This wont catch objects already on the level at startup (which can include players under some circumstances e.g. "host = client")
So, you have to do whatever it is you want to do on those objects manually.

BHS.DLL also contains code that replaces the screenshot code in renegade with new code.
This new code outputs screenshots in PNG format instead of TGA format.
It has been known to cause lag when it is being used, this is a side-effect of the time libpng takes to compress the PNG file and there isnt anything I can do about it.
A PNG file is a compressed image file similar to a JPEG file.
Most image editors that can read and write JPEG files can read and write PNG files.
Web browsers can also display PNG just as easily as they can display JPEG.
PNG was chosen because it takes less time to compress than JPEG.
Plus, the code for compressing PNG files is smaller than that for JPEG 
files (which means BHS.DLL is smaller).
Note that the time taken to read the screen out of your graphics card RAM is also a factor in the sceeenshot lag.

BHS.DLL also contains new keyboard hook code.
On the client (including the server if it is also a client), at startup the keyboard hook code reads in a config file called keys.cfg
Each line in the file looks like this
Jetpack=Y_Key
The part before the = is the logical key name and is what is used by a scropt to refer to the key.
The part after the = is the physical key name and refers to the actual keyboard key tat you press to trigger the particular logical key.
The data is read in from keys.cfg and stored in a structure matching logical keys to physical keys.
Everytime renegade polls the keyboard, the new code iterates through this structure and retrieves the current state of the key.
Then, it goes through some code to make sure that the key hook is only triggered once for every time you press and release the key.
After this, it goes into another hook that checks to see which keys have been pressed (and therefore the server needs to know about the keypress).
It has code to make sure it doesnt trigger when the main menu, dialogs, message box (f2/f3) and console input box are active.
(i.e. if you press the key when those are active, the hook wont trigger)
To install a keyboard hook, you create a KeyHookStruct. This contains the address of the hook, the name of the logical key you are interested in,
the player ID of the player you are interested in plus a user specified data pointer.
(can point to whatever you like, it gets passed back to the hook function).
Then you call AddKeyHook to add the keyboard hook, passing in the KeyHookStruct.
To remove it, you call RemoveKeyHook and pass the return value from AddKeyHook.
The hook function will then trigger every time the specified player presses whatever key they have assigned to that particular logical key (in keys.cfg)
The hook function looks like this:
void KeyHook(void *data)
the value of data is what you passed in when you registered the hook.
Look at JFW_Key_Hook_Base and related scripts in jfwhook.h/jfwhook.cpp to see an example of how to use this.

see keys.txt for details of the valid key names for the keys.cfg config file.
see keycfg.txt for details of the GUI key config application.

bhs.dll also contains code for custom scopes.
Basicly, each infantry unit in your mod can have a different custom scope
Scopes are per-unit (as opposed to per-weapon) because of engine limitations.
There is no real limit on the number of scopes you can have.

Firstly, you create a scope texture for each scope that is square with the width and height a power of 2 (like any other renegade texture). This texture will be "stretched" to fill the screen. Use alpha blending like on the default renegade sniper scope to create the "hole" that you can see through.

Then, you create a scopes.cfg file either in your data folder or in one of the mix files for your mod. This has lines like the following:
scope.tga=Sniper=0.61154997=11.200000
The first part is the texture name (use .tga on the end even if it is a dds file)
The second part is the name of the camera profile to use
The third part is the minumum zoom level to use (how far to zoom out)
The last part is the maximum zoom level to use (how far to zoom in)
The camera settings shown above provide an acceptable camera profile (similar to how the default sniper scope works)

At startup, scopes.cfg is read with each scope (i.e. each line) being given a number starting from 0.
Then, in your mod you put the script JFW_Scope on every player-buyable infantry unit in the game (i.e. anything the player could become) passing either the scope number of -1 (for no scope).
If you wish to change scopes at runtime, you can use something like
JFW_Attach_Script_Custom to attach a new copy of JFW_Scope to change the scope.
JFW_Scope sends a message to the client that initalizes the specified scope.
You can also use MDB_Weapon_Scope (see the readme file for details) to implement per-weapon scope logic.

When the "scope" logical key (set in keys.cfg and used via my keyboard hook code), if the scope is not -1 and the player is not in a vehicle, the scope is switched on. Pressing it again switches it off.
Whilst in scope mode, you can use the "zoom" keys (same as with the normal sniper scope) to zoom in and out.
Also, when the scope is enabled, the Next Weapon, Previous Weapon, First Person Toggle and Action keys are disabled. The weapon switch keys are disabled because the mouse wheel is used for both zoom and for switch weapon. The number keys at the top still work to switch weapons.
The First Person Toggle and Action (i.e. PT, ladder, vehicle entry etc) keys are disabled because they change the camera settings which breaks the scope camera logic. (although if you need to use those keys, just switch out of scope mode)
Some notes:
If you want the normal scope on a unit, put a custom scope of -1 for it. Also, make sure that there is no way for that unit to get a custom scope as having both on the unit will break things. (or just use MDB_Weapon_Scope)
If you put MDB_Weapon_Scope on a unit, dont put JFW_Scope on it and vice versa. If you are using MDB_Weapon_Scope, put that on all units.
All infantry needs either JFW_Scope or MDB_Weapon_Scope on it otherwise bad things will happen.

If you are doing scripting, you can call Set_Scope to change the scope assigned to a player.

There is new logic to change the main HUD texture. Basicly, you put the script JFW_HUD on every infantry unit, passing in the name of a HUD texture.
A HUD texture is a file that is the same layout as HUD_MAIN.TGA. If you want a unit to use HUD_MAIN.TGA, you can pass that, otherwise pass the name
of whatever custom texture you have created for your mod. Every infantry unit must have JFW_HUD on it with some texture otherwise bad things will happen.
When passing the texture, put .tga on the end even if it is a .dds file.

If you are doing scripting, you can call Set_HUD_Texture to change the HUD texture assigned to a player.

There is a new logfile output by bhs.dll with the same name as the regular renlog but with bhs_ at the front.
On windows (client and WFDS) and linux, this will contain file will contain all f2/f3 console messages
In addition, on linux this will contain all team change messages

On the client (dont know if its "host is a client also" or not), there is a log called like the renlog but called client_ instead of renlog_.
This will contain the following:
all uses of MESASGE on the host
all uses of PPAGE on the host for this player
all uses of TPAGE on the host for this team
all uses of TMSG on the host for this team
all f2 chat messages
all f3 chat messages for this team
There will be an indication if its for everyone, team or private (note that because of how it works, a TPAGE command will be marked "private" and not "team", messages sent by the TMSG command will show up as team messages)
This also records messages like "host: xxx changed teams" and "host: xxx committed suicide"

There is also a hook that lets custom scripts.dll mods (e.g. server-side mods) get access to all f2/f3 chat that passes through the server.
You create a function of the form
void Chat_Hook(int PlayerID,int Type,const wchar_t *Message) (note the change from char * in 2.1 to wchar_t * in 2.2)
Then you pass the function to AddChatHook (defined in engine.h)
If the chathook is called Chat_Hook, put the line AddChatHook(Chat_Hook); somewhere in your code. (e.g. somewhere that is called on startup)
Then, the function gets called everytime f2/f3 chat passes through the server.
PlayerID is the player ID of the player that sent the chat.
Type is 2 for player-to-player private message, 1 for team mesasge and 0 for everyone message.
Message is the message itself. If you need to save the message data for later use, dont save the pointer passed into your chat
hook function, copy the data somewhere else.
You can only have one chat hook function registered at any one time. Also, if you want to have no chat hook at all registered, pass NULL to AddChatHook.
This works on the server regardless of if clients have bhs.dll

This functionality is quite useful if you want to make new !xxx commands, especially since you can use the player ID to verify that the person using the command
is authorized to use it. It also means you can do all the things that you would otherwise need new console commands to do.

There is a hook that lets custom scripts.dll mods see all host messages (i.e. all messages done via message, tpage and ppage)
You create a function of the form
void Host_Hook(int PlayerID,int Type,const char *Message)
Then you pass the function to AddHostHook (defined in engine.h)
If the hosthook is called Host_Hook, put the line AddHostHook(Host_Hook); somewhere in your code. (e.g. somewhere that is called on startup)
Then, the function gets called when the message, ppage or tpage console commands are used on the server.
Type is 2 for private message, 1 for team message, 0 for everyone message
Type 3 is for the TMSG console command.
Message is the message itself. If you need to save the message data for later use, dont save the pointer passed into your host
hook function, copy the data somewhere else.
if Type is 2, PlayerID is the player ID of the target
if Type is 1, PlayerID is the team its being sent to (0 = nod, 1 = GDI)
if Type is 0, PlayerID should be ignored
if Type is 3, PlayerID is the player ID of the sender
You can only have one host hook function registered at any one time. Also, if you want to have no host hook at all registered, pass NULL to AddHostHook.
This works on the server regardless of if clients have bhs.dll

There is also an engine call called GetCurrentMusicTrack that returns the same thing as the SONG console command.
There is also an engine call called GetBHSVersion that returns the version of bhs.dll installed on the server.

If the client is running bhs.dll >= 2.2, then when they join, a message will be sent to the server, causing the same output as for the VERSION console command to be output for that player.
The VERSION console command will also work on these players as it does on players with < 2.2

There is also code to fix the invisible harvester bug (where if you have an airstrip, go low power and your harvester is destroyed, it spawns invisible).
However, the downside is that you can see the wheels/treads of the vehicle sticking out of the cargo plane when it flies in.
What normally happens is that the vehicle is created and made invisible and the cinematic is started. When the vehicle is dropped off, it is made visible again.
However, in the bug case, it is not made visible again. The fix stops it from being made invisible in the first place.

The linux version of BHS.DLL also contains a fix to make the IP address display properly in the PLAYER_INFO console command.
All of these last few items work on the server regardless of if the client has bhs.dll installed.

Also, there is now new code in bhs.dll to make the infantry death sound and powerup collection sound play on the client as well as the server.
You need bhs.dll on the server and on the client for this to work.

There is also code in bhs.dll to disable certain network interfaces that could be used by a cheater to run console commands on the server from the client, kill
someone from the client, steal someone elses money and other undesirable things.

And there is code to block people with an invalid nickname.
If the nickname of the new player matches any of the following, the player is denied a connection and a console message listing the IP address of the denied player is displayed:
"Player with invalid nickname blocked, player IP was 1.2.3.4"
The player will then be denied connection.
Blocked nicknames are those with:
Nickname length = 0
Nickname length > 35
Nickname = Hostname
Nickname has non-ascii characters (i.e. below ' ' or above '~')
Nickname is all spaces
Nickname matches a name already in use on the server
This code replaces the existing bandtest.dll fix (so you dont need it anymore, I suggest removing it in case it conflicts).
Credit goes to Slent_Kane for releasing the first working fix for the nickname exploits.

These 2 fixes dont require bhs.dll on the client and will work on the Linux FDS (unlike the bandtest.dll fix)

There is new code on windows (client, FDS, server, whatever) that, when renegade crashes, will dump a new crashdump.txt file.
The old _except.txt stuff is gone as is any apperance of the "renegade crashed at address xxx" windows crash dialogs that used to appear.
Now, if it crashes, it will exit silently and spit out a crashdump.txt file in your renegade folder.
This file contains the following:
The address the exception occured at (including which module and which segment of that module the address resides in)
The address being accessed when the crash happens (including if it was trying to read from that address or write to it)
Whether the crash happened with the game client or the FDS
The contents of the CPU registers at the time the crash happened
The contents of the FPU registers at the time the crash happened
The bytes currently in memory at the address the crash happened at
The CRC32 checksum of bhs.dll, scripts.dll, scripts2.dll and bandtest.dll (I check bandtest.dll in case someone is using something from SK that changes it)
The current version of bhs.dll (i.e. the same thing printed by the sversion/version console commands)
The current version of windows (specifically all the return values from GetVersionEx)
The current time and date (great for matching crashdump.txt files with events in a server log file
A complete list of all modules loaded into the renegade process space along with the address they are loaded at
And a complete dump of the processor stack at the time the crash happened (along with details of which module and which segment of that module the address resides in)

There is also a hook that lets custom scripts.dll mods (e.g. server-side mods) hook whenever a player joins the server
You create a function of the form
void Player_Join_Hook(int PlayerID,const char *PlayerName)
Then you pass the function to AddPlayerJoinHook (defined in engine.h)
Then, the function gets called everytime a player joins the server
PlayerID is the player ID of the player that just joined
PlayerName is the player name of the player that just joined.
I dont know if the player has a valid GameObject at this point, you could try calling Get_GameObj and finding out if it returns something other than 0.
If you need to save the player name for later use, dont save the pointer passed into your hook function, copy the data somewhere else.
You can only have one player join hook function registered at any one time. Also, if you want to have no hook at all registered, pass NULL to AddPlayerJoinHook.
This works on the server regardless of if clients have bhs.dll

There is also a hook that lets custom scripts.dll mods (e.g. server-side mods) hook on level load
You create a function of the form
void Load_Level_Hook()
Then you pass the function to AddLoadLevelHook (defined in engine.h)
Then, the function gets called when a map is loaded.
All the GameObjects are valid at this point so functions like Find_Soldier_Factory (for example) will work and return valid data.
You can only have one load level hook function registered at any one time. Also, if you want to have no hook at all registered, pass NULL to AddLoadLevelHook.
This works on the server regardless of if clients have bhs.dll

There is also a hook that lets custom scripts.dll mods (e.g. server-side mods) hook on gameover
You create a function of the form
void Game_Over_Hook()
Then you pass the function to AddGameOverHook (defined in engine.h)
Then, the function gets called when the game ends.
Use such things as The_Game()->WinnerID, The_Game()->WinType,Get_Team_Score() & Tally_Team_Size() to retrieve information related to who won etc
You can only have one game over hook function registered at any one time. Also, if you want to have no hook at all registered, pass NULL to AddGameOverHook.
This works on the server regardless of if clients have bhs.dll

There is new code that lets you change the color used for the teams (i.e. red for nod and yellow for gdi)
This requires bhs.dll on the client.
You create a hud.ini file that looks something like this
[General]
NodHouseRed=255
NodHouseGreen=0
NodHouseBlue=0
GDIHouseRed=255
GDIHouseGreen=204
GDIHouseBlue=0

(for reference, the numbers above are the default numbers).
This ini file will be read by bhs.dll (if it exists) and the colors defined therein (if any values are defined) will be used instead of the default colors.
As the name suggests, hud.ini will be used for more in-game user interface related things in the future :)

There is also code to disable the GameSpy CD check in the game client, WFDS and LFDS. This was done because:
A.The The First Decade keys are not in the GameSpy database.
B.We needed it for the LFDS WOL code
and C.XWIS doesnt check CD keys anyway.
