banner
navBar home resume scripting
progNavBar Programing CS
Game/Program:
"Unity Battle Space" VBS Plugin DLL
 

This is the VBS plugin DLL portion of the Unity Battle Space project. It is written in C++ in conjunction with the SDL_Net API (networking portion of the SDL API) which allowed me to do TCP/IP programming much more easily.

The VBS plugin client parses server commands and then constructs VBS script commands as strings to be sent in game (and vice versa).

The C# portions of this project can be found HERE in the C# section.

 
vbsplugin.h
#ifndef _VBS_PLUGIN_H__
#define _VBS_PLUGIN_H__

#define VBSPLUGIN_EXPORT __declspec(dllexport)

VBSPLUGIN_EXPORT void WINAPI RegisterHWND(void *hwnd, void *hins); //handlw to vbs2 window instance in the event of any graphical interface is implemented via plugin
VBSPLUGIN_EXPORT void WINAPI RegisterCommandFnc(void *executeCommandFnc); //passes the function the dll must call to execute vbs2 script commands
VBSPLUGIN_EXPORT void WINAPI OnSimulationStep(float deltaT); //called each frame, try not to do too many intensive things in this method as it will effect framerate
VBSPLUGIN_EXPORT const char* WINAPI PluginFunction(const char *input); //gets called when the vbs2 function of the same name is called

#endif
VBSPlugin.cpp
//no idea what this does but needed for sdl i think/////////////////////////
#if 0
#!/bin/sh
gcc -Wall `sdl-config --cflags` tcpc.c -o tcpc `sdl-config --libs` -lSDL_net
 
exit
#endif
//////////////////////////////////////////////////////////////////////////////

//defs
#define BUFFER_SIZE 512

//libs
#include <windows.h> //needed for a good chunk of windows specific methods
#include <string.h> //string object
#include <iostream> //needed for std::
#include <exception> //exception object
#include <sstream> //string stream object
#include <string> //string object
#include <fstream> //file io

#include "VBSPlugin.h" //our header (all it does is import dll methods)
#include "SDL_net.h" //sdl networking

//prototypes
std::string checkServer(Uint16 _port, std::string _sHost); //check the server for instruction
void updateServer(Uint16 _port, std::string _sHost, std::string _strCommand); //send command to server

//command function declaration
typedef int (WINAPI *ExecuteCommandType) (const char *command, char *result, int resultLength);

//command function definition
ExecuteCommandType ExecuteCommand=NULL; //this will hold callback functions passed from vbs2 during RegisterCommandFunc

VBSPLUGIN_EXPORT void WINAPI RegisterCommandFnc(void *executeCommandFnc)
{
    ExecuteCommand=(ExecuteCommandType)executeCommandFnc;    
}

//shows how executeCommandFnc gets stored as ExecuteCommand which is used to invoke script commands in vbs2
VBSPLUGIN_EXPORT void WINAPI OnSimulationStep(float deltaT)
{
    ExecuteCommand("_ret=pluginFunction [\"VBSPlugin\", applicationState select 1]", NULL, 0); //ask vbs for it's application state        

    ExecuteCommand("_ret=pluginFunction [\"VBSPlugin\", getDir (vehicle player)]", NULL, 0); //get hull rotation from vbs
}

/*example, in vbs2 the following script command:
    ret=pluginFunction["PLUGINNAME", "text or value"]

  -the "PLUGINNAME" is the name of the plugin without ".dll"
  -the "text or value" is passed as const char *input which is the argument for the cpp function PluginFunction
  -it is then parsed as necessary, appropriate operations made based up on it, and the resulting command to be sent back to vbs is returned to the ret var in the
  vbs2 script.*/

VBSPLUGIN_EXPORT const char* WINAPI PluginFunction(const char *input)
{
    try
    {
        std::string strInput=input; //place char array into string
        
        if (strInput=="MISSION")
        {
            std::string strResponse=checkServer(8002, "127.0.0.1");
            if (strResponse!="neg") {ExecuteCommand(strResponse.c_str(), NULL, 0);}            
        }
        //if the input is numeric
        else if (strInput[0]=='0' || strInput[0]=='1' || strInput[0]=='2' || strInput[0]=='3' || strInput[0]=='4' || strInput[0]=='5' || strInput[0]=='6' ||
                 strInput[0]=='7' || strInput[0]=='8' || strInput[0]=='9')
        {
            updateServer(8003, "127.0.0.1", "SERVER set hullRot "+strInput); //give the server the hull rot            
        }

        static const char result[]="[1.0, 3.75]"; //place something in the output (presently these values are meaningless)
        return result; //return it to vbs2
    }
    catch (std::exception &e)
    {
        //std::string except=e.what();
        //OutputDebugString(except.c_str());
    }
}

BOOL WINAPI DllMain(HINSTANCE hDll, DWORD fdwReason, LPVOID lpvReserved) //main function
{
    switch(fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            OutputDebugString("Called DllMain with DLL PROCESS ATTACH\n");
            break;
        case DLL_PROCESS_DETACH:
            OutputDebugString("Called DllMain with DLL PROCESS DETACH\n");
            break;
        case DLL_THREAD_ATTACH:
            OutputDebugString("Called DllMain with DLL THREAD ATTACH\n");
            break;
        case DLL_THREAD_DETACH:
            OutputDebugString("Called DllMain with DLL THREAD DETACH\n");
            break;
        default:
            break;
    }

    return TRUE;
}

std::string checkServer(Uint16 _port, std::string _sHost) //check server for instruction
{
    
    if (SDLNet_Init() < 0) //throw a fit if sdl networking doesn't initialize
    {
        std::stringstream ssException;
        ssException << "checkServer(Uint16, std::string) -- SDLNet_Init() error: " << SDLNet_GetError();        
        throw std::exception(ssException.str().c_str());        
    }

    IPaddress ip; //holds ip addy
    TCPsocket sd; //holds socket descriptor
    short int len; //length of message string
    char buffer[15]; //holds outgoing message
    char receiveBuffer[BUFFER_SIZE]; //receive buffer
    std::string strSend="what about now?"; //server query string
    std::string strReceive=""; //holds message from server

    if (SDLNet_ResolveHost(&ip, _sHost.c_str(), _port)) //throw a fit if we can't resolve our host
    {
        std::stringstream ssException;
        ssException << "checkServer(Uint16, std::string) -- SDLNet_ResolveHost() error: " << SDLNet_GetError();        
        throw std::exception(ssException.str().c_str());        
    }

    if (!(sd = SDLNet_TCP_Open(&ip))) //throw a fit if we can't open a connection with the server
    {
        std::stringstream ssException;        
        ssException << "checkServer(Uint16, std::string) -- SDLNet_TCP_OPEN() error: " << SDLNet_GetError();
        throw std::exception(ssException.str().c_str());
    }
    
    for (unsigned short iClean=0; iClean<15; iClean++) {buffer[iClean]=' ';} //init buffer

    for (unsigned short iString=0; iString<strSend.length(); iString++) {buffer[iString]=strSend[iString];} //dump query string into buffer
    
    len=strlen(buffer); //set length of string
    if (SDLNet_TCP_Send(sd, (void *)buffer, len)<len) //send message to server and throw a fit if we can't
    {
        std::stringstream ssException;
        ssException << "checkServer(Uint16, std::string) -- SDLNet_TCP_SEND() error: " << SDLNet_GetError();        
        throw std::exception(ssException.str().c_str());
    }

    for (unsigned short iBuffer=0; iBuffer<BUFFER_SIZE; iBuffer++) {receiveBuffer[iBuffer]=' ';} //clean receive buffer

    if (SDLNet_TCP_Recv(sd, receiveBuffer, BUFFER_SIZE) > 0) //grab server response
    {
        for (unsigned short iBuffer=0; iBuffer<BUFFER_SIZE; iBuffer++) //loop through what we got
        {
            if (receiveBuffer[iBuffer]!='~') {strReceive+=receiveBuffer[iBuffer];} //append to receive string if not at end of received command
            else {iBuffer=9999;} //otherwise early exit of for loop
        }
    }    

    SDLNet_TCP_Close(sd); //close connection
    SDLNet_Quit(); //shut down sdl networking

    return strReceive;
}

void updateServer(Uint16 _port, std::string _sHost, std::string _strCommand) //send command to server
{
    if (SDLNet_Init() < 0) //throw a fit if sdl networking doesn't initialize
    {
        std::stringstream ssException;
        ssException << "updateServer(Uint16, std::string, std::string) -- SDLNet_Init() error: " << SDLNet_GetError();        
        throw std::exception(ssException.str().c_str());        
    }

    IPaddress ip; //holds ip addy
    TCPsocket sd; //holds socket descriptor
    short int len; //length of message string
    char buffer[BUFFER_SIZE]; //holds outgoing message
    char receiveBuffer[BUFFER_SIZE]; //receive buffer
    std::string strSend=_strCommand; //command to send to server
    std::string strReceive=""; //holds message from server

    if (SDLNet_ResolveHost(&ip, _sHost.c_str(), _port)) //throw a fit if we can't resolve our host
    {
        std::stringstream ssException;
        ssException << "updateServer(Uint16, std::string, std::string) -- SDLNet_ResolveHost() error: " << SDLNet_GetError();        
        throw std::exception(ssException.str().c_str());        
    }

    if (!(sd = SDLNet_TCP_Open(&ip))) //throw a fit if we can't open a connection with the server
    {
        std::stringstream ssException;        
        ssException << "updateServer(Uint16, std::string, std::string) -- SDLNet_TCP_OPEN() error: " << SDLNet_GetError();
        throw std::exception(ssException.str().c_str());
    }
    
    for (unsigned short iClean=0; iClean<BUFFER_SIZE; iClean++) {buffer[iClean]=' ';} //init buffer

    for (unsigned short iString=0; iString<strSend.length(); iString++) {buffer[iString]=strSend[iString];} //dump command string into buffer
    
    len=strlen(buffer);//set length of string
    if (SDLNet_TCP_Send(sd, (void *)buffer, len)<len) //send message to server and throw a fit if we can't
    {
        std::stringstream ssException;
        ssException << "updateServer(Uint16, std::string, std::string) -- SDLNet_TCP_SEND() error: " << SDLNet_GetError();        
        throw std::exception(ssException.str().c_str());
    }

    for (unsigned short iBuffer=0; iBuffer<BUFFER_SIZE; iBuffer++) {receiveBuffer[iBuffer]=' ';} //clean receive buffer

    if (SDLNet_TCP_Recv(sd, receiveBuffer, BUFFER_SIZE) > 0) //grab server response
    {
        for (unsigned short iBuffer=0; iBuffer<BUFFER_SIZE; iBuffer++) //loop through what we got
        {
            if (receiveBuffer[iBuffer]!='~') {strReceive+=receiveBuffer[iBuffer];} //append to receive string if not at end of received command
            else {iBuffer=9999;} //otherwise early exit of for loop
        }
    }    

    SDLNet_TCP_Close(sd); //close connection
    SDLNet_Quit(); //shut down sdl networking

    //return strReceive;
}
 
^RETURN TO TOP^