Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

59
Engine/source/app/auth.h Normal file
View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _AUTH_H_
#define _AUTH_H_
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
/// Formerly contained a certificate, showing that something was valid.
class Auth2Certificate
{
U32 xxx;
};
/// Formerly contained data indicating whether a user is valid.
struct AuthInfo
{
enum {
MaxNameLen = 31,
};
bool valid;
char name[MaxNameLen + 1];
};
/// Formerly validated the server's authentication info.
inline bool validateAuthenticatedServer()
{
return true;
}
/// Formerly validated the client's authentication info.
inline bool validateAuthenticatedClient()
{
return true;
}
#endif

View file

@ -0,0 +1,331 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/stringFunctions.h"
#include "console/consoleTypes.h"
#include "app/badWordFilter.h"
#include "core/module.h"
#include "console/engineAPI.h"
MODULE_BEGIN( BadWordFilter )
MODULE_INIT
{
BadWordFilter::create();
}
MODULE_SHUTDOWN
{
BadWordFilter::destroy();
}
MODULE_END;
BadWordFilter *gBadWordFilter = NULL;
bool BadWordFilter::filteringEnabled = true;
BadWordFilter::BadWordFilter()
{
VECTOR_SET_ASSOCIATION( filterTables );
dStrcpy(defaultReplaceStr, "knqwrtlzs");
filterTables.push_back(new FilterTable);
curOffset = 0;
}
BadWordFilter::~BadWordFilter()
{
for(U32 i = 0; i < filterTables.size(); i++)
delete filterTables[i];
}
void BadWordFilter::create()
{
Con::addVariable("pref::enableBadWordFilter", TypeBool, &filteringEnabled,
"@brief If true, the bad word filter will be enabled.\n\n"
"@ingroup Game");
gBadWordFilter = new BadWordFilter;
gBadWordFilter->addBadWord("shit");
gBadWordFilter->addBadWord("fuck");
gBadWordFilter->addBadWord("cock");
gBadWordFilter->addBadWord("bitch");
gBadWordFilter->addBadWord("cunt");
gBadWordFilter->addBadWord("nigger");
gBadWordFilter->addBadWord("bastard");
gBadWordFilter->addBadWord("dick");
gBadWordFilter->addBadWord("whore");
gBadWordFilter->addBadWord("goddamn");
gBadWordFilter->addBadWord("asshole");
}
void BadWordFilter::destroy()
{
delete gBadWordFilter;
gBadWordFilter = NULL;
}
U8 BadWordFilter::remapTable[257] = "------------------------------------------------OI---------------ABCDEFGHIJKLMNOPQRSTUVWXYZ------ABCDEFGHIJKLMNOPQRSTUVWXYZ-----C--F--TT--S-C-Z-----------S-C-ZY--CLOY-S-CA---R---UT-UP--IO-----AAAAAAACEEEEIIIIDNOOOOOXOUUUUYDBAAAAAAACEEEEIIIIDNOOOOO-OUUUUYDY";
U8 BadWordFilter::randomJunk[MaxBadwordLength+1] = "REMsg rk34n4ksqow;xnskq;KQoaWnZa";
BadWordFilter::FilterTable::FilterTable()
{
for(U32 i = 0; i < 26; i++)
nextState[i] = TerminateNotFound;
}
bool BadWordFilter::addBadWord(const char *cword)
{
FilterTable *curFilterTable = filterTables[0];
// prescan the word to see if it has any skip chars
const U8 *word = (const U8 *) cword;
const U8 *walk = word;
if(dStrlen(cword) > MaxBadwordLength)
return false;
while(*walk)
{
if(remapTable[*walk] == '-')
return false;
walk++;
}
while(*word)
{
U8 remap = remapTable[*word] - 'A';
U16 state = curFilterTable->nextState[remap];
if(state < TerminateNotFound)
{
// this character is already in the state table...
curFilterTable = filterTables[state];
}
else if(state == TerminateFound)
{
// a subset of this word is already in the table...
// exit out.
return false;
}
else if(state == TerminateNotFound)
{
if(word[1])
{
curFilterTable->nextState[remap] = filterTables.size();
filterTables.push_back(new FilterTable);
curFilterTable = filterTables[filterTables.size() - 1];
}
else
curFilterTable->nextState[remap] = TerminateFound;
}
word++;
}
return true;
}
bool BadWordFilter::setDefaultReplaceStr(const char *str)
{
U32 len = dStrlen(str);
if(len < 2 || len >= sizeof(defaultReplaceStr))
return false;
dStrcpy(defaultReplaceStr, str);
return true;
}
void BadWordFilter::filterString(char *cstring, const char *replaceStr)
{
if(!replaceStr)
replaceStr = defaultReplaceStr;
U8 *string = (U8 *) cstring;
U8 *starts[MaxBadwordLength];
U8 *curStart = string;
U32 replaceLen = dStrlen(replaceStr);
while(*curStart)
{
FilterTable *curFilterTable = filterTables[0];
S32 index = 0;
U8 *walk = curStart;
while(*walk)
{
U8 remap = remapTable[*walk];
if(remap != '-')
{
starts[index++] = walk;
U16 table = curFilterTable->nextState[remap - 'A'];
if(table < TerminateNotFound)
curFilterTable = filterTables[table];
else if(table == TerminateNotFound)
{
curStart++;
break;
}
else // terminate found
{
for(U32 i = 0; i < index; i++)
{
starts[i][0] = (U8 )replaceStr[curOffset % replaceLen];
curOffset += randomJunk[curOffset & (MaxBadwordLength - 1)];
}
curStart = walk + 1;
break;
}
}
walk++;
}
if(!*walk)
curStart++;
}
}
bool BadWordFilter::containsBadWords(const char *cstring)
{
U8 *string = (U8 *) cstring;
U8 *curStart = string;
while(*curStart)
{
FilterTable *curFilterTable = filterTables[0];
U8 *walk = curStart;
while(*walk)
{
U8 remap = remapTable[*walk];
if(remap != '-')
{
U16 table = curFilterTable->nextState[remap - 'A'];
if(table < TerminateNotFound)
curFilterTable = filterTables[table];
else if(table == TerminateNotFound)
{
curStart++;
break;
}
else // terminate found
return true;
}
walk++;
}
if(!*walk)
curStart++;
}
return false;
}
DefineEngineFunction(addBadWord, bool, (const char* badWord),,
"@brief Add a string to the bad word filter\n\n"
"The bad word filter is a table containing words which will not be "
"displayed in chat windows. Instead, a designated replacement string will be displayed. "
"There are already a number of bad words automatically defined.\n\n"
"@param badWord Exact text of the word to restrict.\n"
"@return True if word was successfully added, false if the word or a subset of it already exists in the table\n"
"@see filterString()\n\n"
"@tsexample\n"
"// In this game, \"Foobar\" is banned\n"
"%badWord = \"Foobar\";\n\n"
"// Returns true, word was successfully added\n"
"addBadWord(%badWord);\n\n"
"// Returns false, word has already been added\n"
"addBadWord(\"Foobar\");"
"@endtsexample\n"
"@ingroup Game")
{
return gBadWordFilter->addBadWord(badWord);
}
DefineEngineFunction(filterString, const char *, (const char* baseString, const char* replacementChars), (NULL, NULL),
"@brief Replaces the characters in a string with designated text\n\n"
"Uses the bad word filter to determine which characters within the string will be replaced.\n\n"
"@param baseString The original string to filter.\n"
"@param replacementChars A string containing letters you wish to swap in the baseString.\n"
"@return The new scrambled string \n"
"@see addBadWord()\n"
"@see containsBadWords()\n"
"@tsexample\n"
"// Create the base string, can come from anywhere\n"
"%baseString = \"Foobar\";\n\n"
"// Create a string of random letters\n"
"%replacementChars = \"knqwrtlzs\";\n\n"
"// Filter the string\n"
"%newString = filterString(%baseString, %replacementChars);\n\n"
"// Print the new string to console\n"
"echo(%newString);"
"@endtsexample\n"
"@ingroup Game")
{
const char *replaceStr = NULL;
if(replacementChars)
replaceStr = replacementChars;
else
replaceStr = gBadWordFilter->getDefaultReplaceStr();
char *ret = Con::getReturnBuffer(dStrlen(baseString) + 1);
dStrcpy(ret, baseString);
gBadWordFilter->filterString(ret, replaceStr);
return ret;
}
DefineEngineFunction(containsBadWords, bool, (const char* text),,
"@brief Checks to see if text is a bad word\n\n"
"The text is considered to be a bad word if it has been added to the bad word filter.\n\n"
"@param text Text to scan for bad words\n"
"@return True if the text has bad word(s), false if it is clean\n"
"@see addBadWord()\n"
"@see filterString()\n"
"@tsexample\n"
"// In this game, \"Foobar\" is banned\n"
"%badWord = \"Foobar\";\n\n"
"// Add a banned word to the bad word filter\n"
"addBadWord(%badWord);\n\n"
"// Create the base string, can come from anywhere like user chat\n"
"%userText = \"Foobar\";\n\n"
"// Create a string of random letters\n"
"%replacementChars = \"knqwrtlzs\";\n\n"
"// If the text contains a bad word, filter it before printing\n"
"// Otherwise print the original text\n"
"if(containsBadWords(%userText))\n"
"{\n"
" // Filter the string\n"
" %filteredText = filterString(%userText, %replacementChars);\n\n"
" // Print filtered text\n"
" echo(%filteredText);\n"
"}\n"
"else\n"
" echo(%userText);\n\n"
"@endtsexample\n"
"@ingroup Game")
{
return gBadWordFilter->containsBadWords(text);
}

View file

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _H_BADWORDFILTER
#define _H_BADWORDFILTER
#include "core/util/tVector.h"
class BadWordFilter
{
private:
struct FilterTable
{
U16 nextState[26]; // only 26 alphabetical chars.
FilterTable();
};
friend struct FilterTable;
Vector<FilterTable*> filterTables;
enum {
TerminateNotFound = 0xFFFE,
TerminateFound = 0xFFFF,
MaxBadwordLength = 32,
};
char defaultReplaceStr[32];
BadWordFilter();
~BadWordFilter();
U32 curOffset;
static U8 remapTable[257];
static U8 randomJunk[MaxBadwordLength + 1];
static bool filteringEnabled;
public:
bool addBadWord(const char *word);
bool setDefaultReplaceStr(const char *str);
const char* getDefaultReplaceStr(){ return defaultReplaceStr; }
void filterString(char *string, const char *replaceStr = NULL);
bool containsBadWords(const char *string);
static bool isEnabled() { return filteringEnabled; }
static void setEnabled(bool enable) { filteringEnabled = enable; }
static void create();
static void destroy();
};
extern BadWordFilter *gBadWordFilter;
#endif

View file

@ -0,0 +1,301 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "app/banList.h"
#include "core/stream/fileStream.h"
#include "core/module.h"
#include "console/engineAPI.h"
IMPLEMENT_STATIC_CLASS( BanList,, "Functions for maintaing a list of banned users." );
ConsoleDoc(
"@class BanList\n"
"@ingroup Miscellaneous\n"
"@brief Used for kicking and banning players from a server.\n"
"There is only a single instance of BanList. It is very important to note that you do not ever create this object in script "
"like you would other game play objects. You simply reference it via namespace.\n\n"
"For this to be used effectively, make sure you are hooking up other functions to BanList. "
"For example, functions like GameConnection::onConnectRequestRejected( %this, %msg ) and function GameConnection::onConnectRequest are excellent "
"places to make use of the BanList. Other systems can be used in conjunction for strict control over a server\n\n"
"@see addBadWord\n"
"@see containsBadWords\n"
);
BanList* BanList::smInstance;
MODULE_BEGIN( BanList )
MODULE_INIT
{
new BanList;
}
MODULE_SHUTDOWN
{
delete BanList::instance();
}
MODULE_END;
//------------------------------------------------------------------------------
BanList::BanList()
{
AssertFatal( !smInstance, "BanList::BanList - already instantiated" );
VECTOR_SET_ASSOCIATION( list );
smInstance = this;
}
//------------------------------------------------------------------------------
void BanList::addBan(S32 uniqueId, const char *TA, S32 banTime)
{
S32 curTime = Platform::getTime();
if(banTime != 0 && banTime < curTime)
return;
// make sure this bastard isn't already banned on this server
Vector<BanInfo>::iterator i;
for(i = list.begin();i != list.end();i++)
{
if(uniqueId == i->uniqueId)
{
i->bannedUntil = banTime;
return;
}
}
BanInfo b;
dStrcpy(b.transportAddress, TA);
b.uniqueId = uniqueId;
b.bannedUntil = banTime;
if(!dStrnicmp(b.transportAddress, "ip:", 3))
{
char *c = dStrchr(b.transportAddress+3, ':');
if(c)
{
*(c+1) = '*';
*(c+2) = 0;
}
}
list.push_back(b);
}
//------------------------------------------------------------------------------
void BanList::addBanRelative(S32 uniqueId, const char *TA, S32 numSeconds)
{
S32 curTime = Platform::getTime();
S32 banTime = 0;
if(numSeconds != -1)
banTime = curTime + numSeconds;
addBan(uniqueId, TA, banTime);
}
//------------------------------------------------------------------------------
void BanList::removeBan(S32 uniqueId, const char *)
{
Vector<BanInfo>::iterator i;
for(i = list.begin();i != list.end();i++)
{
if(uniqueId == i->uniqueId)
{
list.erase(i);
return;
}
}
}
//------------------------------------------------------------------------------
bool BanList::isBanned(S32 uniqueId, const char *)
{
S32 curTime = Platform::getTime();
Vector<BanInfo>::iterator i;
for(i = list.begin();i != list.end();)
{
if(i->bannedUntil != 0 && i->bannedUntil < curTime)
{
list.erase(i);
continue;
}
else if(uniqueId == i->uniqueId)
return true;
i++;
}
return false;
}
//------------------------------------------------------------------------------
bool BanList::isTAEq(const char *bannedTA, const char *TA)
{
char a, b;
for(;;)
{
a = *bannedTA++;
b = *TA++;
if(a == '*' || (!a && b == ':')) // ignore port
return true;
if(dTolower(a) != dTolower(b))
return false;
if(!a)
return true;
}
}
//------------------------------------------------------------------------------
void BanList::exportToFile(const char *name)
{
FileStream *banlist;
char filename[1024];
Con::expandScriptFilename(filename, sizeof(filename), name);
if((banlist = FileStream::createAndOpen( filename, Torque::FS::File::Write )) == NULL)
return;
char buf[1024];
Vector<BanInfo>::iterator i;
for(i = list.begin(); i != list.end(); i++)
{
dSprintf(buf, sizeof(buf), "BanList::addAbsolute(%d, \"%s\", %d);\r\n", i->uniqueId, i->transportAddress, i->bannedUntil);
banlist->write(dStrlen(buf), buf);
}
delete banlist;
}
//=============================================================================
// API.
//=============================================================================
// MARK: ---- API ----
//-----------------------------------------------------------------------------
DefineEngineStaticMethod( BanList, addAbsolute, void, ( S32 uniqueId, const char* transportAddress, S32 banTime ),,
"Ban a user until a given time.\n\n"
"@param uniqueId Unique ID of the player.\n"
"@param transportAddress Address from which the player connected.\n"
"@param banTime Time at which they will be allowed back in."
"@tsexample\n"
"// Kick someone off the server\n"
"// %client - This is the connection to the person we are kicking\n"
"function kick(%client)\n"
"{\n"
" // Let the server know what happened\n"
" messageAll( 'MsgAdminForce', '\\c2The Admin has kicked %1.', %client.playerName);\n\n"
" // If it is not an AI Player, execute the ban.\n"
" if (!%client.isAIControlled())\n"
" BanList::addAbsolute(%client.guid, %client.getAddress(), $pref::Server::KickBanTime);\n\n"
" // Let the player know they messed up\n"
" %client.delete(\"You have been kicked from this server\");\n"
"}\n"
"@endtsexample\n\n")
{
BanList::instance()->addBan( uniqueId, transportAddress, banTime );
}
//-----------------------------------------------------------------------------
DefineEngineStaticMethod( BanList, add, void, ( S32 uniqueId, const char* transportAddress, S32 banLength ),,
"Ban a user for banLength seconds.\n\n"
"@param uniqueId Unique ID of the player.\n"
"@param transportAddress Address from which the player connected.\n"
"@param banLength Time period over which to ban the player."
"@tsexample\n"
"// Kick someone off the server\n"
"// %client - This is the connection to the person we are kicking\n"
"function kick(%client)\n"
"{\n"
" // Let the server know what happened\n"
" messageAll( 'MsgAdminForce', '\\c2The Admin has kicked %1.', %client.playerName);\n\n"
" // If it is not an AI Player, execute the ban.\n"
" if (!%client.isAIControlled())\n"
" BanList::add(%client.guid, %client.getAddress(), $pref::Server::KickBanTime);\n\n"
" // Let the player know they messed up\n"
" %client.delete(\"You have been kicked from this server\");\n"
"}\n"
"@endtsexample\n\n")
{
BanList::instance()->addBanRelative( uniqueId, transportAddress, banLength );
}
//-----------------------------------------------------------------------------
DefineEngineStaticMethod( BanList, removeBan, void, ( S32 uniqueId, const char* transportAddress ),,
"Unban someone.\n\n"
"@param uniqueId Unique ID of the player.\n"
"@param transportAddress Address from which the player connected.\n"
"@tsexample\n"
"BanList::removeBan(%userID, %ipAddress);\n"
"@endtsexample\n\n")
{
BanList::instance()->removeBan( uniqueId, transportAddress );
}
//-----------------------------------------------------------------------------
DefineEngineStaticMethod( BanList, isBanned, bool, ( S32 uniqueId, const char* transportAddress ),,
"Is someone banned?\n\n"
"@param uniqueId Unique ID of the player.\n"
"@param transportAddress Address from which the player connected.\n\n"
"@tsexample\n"
"//-----------------------------------------------------------------------------\n"
"// This script function is called before a client connection\n"
"// is accepted. Returning "" will accept the connection,\n"
"// anything else will be sent back as an error to the client.\n"
"// All the connect args are passed also to onConnectRequest\n"
"function GameConnection::onConnectRequest( %client, %netAddress, %name )\n"
"{\n"
" // Find out who is trying to connect\n"
" echo(\"Connect request from: \" @ %netAddress);\n\n"
" // Are they allowed in?\n"
" if(BanList::isBanned(%client.guid, %netAddress))\n"
" return \"CR_YOUAREBANNED\";\n\n"
" // Is there room for an unbanned player?\n"
" if($Server::PlayerCount >= $pref::Server::MaxPlayers)\n"
" return \"CR_SERVERFULL\";\n"
" return "";\n"
"}\n"
"@endtsexample\n\n")
{
return BanList::instance()->isBanned( uniqueId, transportAddress );
}
//-----------------------------------------------------------------------------
DefineEngineStaticMethod( BanList, export, void, ( const char* filename ),,
"Dump the banlist to a file.\n\n"
"@param filename Path of the file to write the list to.\n\n"
"@tsexample\n"
"BanList::Export(\"./server/banlist.cs\");\n"
"@endtsexample\n\n")
{
BanList::instance()->exportToFile( filename );
}

View file

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BANLIST_H_
#define _BANLIST_H_
#ifndef _ENGINEAPI_H_
#include "console/engineAPI.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
/// Helper class to keep track of bans.
class BanList
{
public:
DECLARE_STATIC_CLASS( BanList );
struct BanInfo
{
S32 uniqueId;
char transportAddress[128];
S32 bannedUntil;
};
protected:
Vector< BanInfo > list;
static BanList* smInstance;
public:
BanList();
static BanList* instance() { return smInstance; }
void addBan(S32 uniqueId, const char *TA, S32 banTime);
void addBanRelative(S32 uniqueId, const char *TA, S32 numSeconds);
void removeBan(S32 uniqueId, const char *TA);
bool isBanned(S32 uniqueId, const char *TA);
bool isTAEq(const char *bannedTA, const char *TA);
void exportToFile(const char *fileName);
};
#endif

258
Engine/source/app/game.cpp Normal file
View file

@ -0,0 +1,258 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/platformInput.h"
#include "app/game.h"
#include "math/mMath.h"
#include "core/dnet.h"
#include "core/stream/fileStream.h"
#include "core/frameAllocator.h"
#include "core/iTickable.h"
#include "core/strings/findMatch.h"
#include "console/simBase.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "gui/controls/guiMLTextCtrl.h"
#ifdef TORQUE_TGB_ONLY
#include "T2D/oldModel/networking/t2dGameConnection.h"
#include "T2D/oldModel/networking/t2dNetworkServerSceneProcess.h"
#include "T2D/oldModel/networking/t2dNetworkClientSceneProcess.h"
#else
#include "T3D/gameBase/gameConnection.h"
#include "T3D/gameFunctions.h"
#include "T3D/gameBase/gameProcess.h"
#endif
#include "platform/profiler.h"
#include "gfx/gfxCubemap.h"
#include "gfx/gfxTextureManager.h"
#include "sfx/sfxSystem.h"
#ifdef TORQUE_PLAYER
// See matching #ifdef in editor/editor.cpp
bool gEditingMission = false;
#endif
//--------------------------------------------------------------------------
ConsoleFunctionGroupBegin( InputManagement, "Functions that let you deal with input from scripts" );
ConsoleFunction( deactivateDirectInput, void, 1, 1, "()"
"@brief Disables DirectInput.\n\n"
"Also deactivates any connected joysticks.\n\n"
"@ingroup Input" )
{
TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
if ( Input::isActive() )
Input::deactivate();
}
ConsoleFunction( activateDirectInput, void, 1, 1,"()"
"@brief Activates DirectInput.\n\n"
"Also activates any connected joysticks."
"@ingroup Input")
{
TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
if ( !Input::isActive() )
Input::activate();
}
ConsoleFunctionGroupEnd( InputManagement );
//--------------------------------------------------------------------------
static const U32 MaxPlayerNameLength = 16;
ConsoleFunction( strToPlayerName, const char*, 2, 2, "strToPlayerName( string )" )
{
TORQUE_UNUSED(argc);
const char* ptr = argv[1];
// Strip leading spaces and underscores:
while ( *ptr == ' ' || *ptr == '_' )
ptr++;
U32 len = dStrlen( ptr );
if ( len )
{
char* ret = Con::getReturnBuffer( MaxPlayerNameLength + 1 );
char* rptr = ret;
ret[MaxPlayerNameLength - 1] = '\0';
ret[MaxPlayerNameLength] = '\0';
bool space = false;
U8 ch;
while ( *ptr && dStrlen( ret ) < MaxPlayerNameLength )
{
ch = (U8) *ptr;
// Strip all illegal characters:
if ( ch < 32 || ch == ',' || ch == '.' || ch == '\'' || ch == '`' )
{
ptr++;
continue;
}
// Don't allow double spaces or space-underline combinations:
if ( ch == ' ' || ch == '_' )
{
if ( space )
{
ptr++;
continue;
}
else
space = true;
}
else
space = false;
*rptr++ = *ptr;
ptr++;
}
*rptr = '\0';
//finally, strip out the ML text control chars...
return GuiMLTextCtrl::stripControlChars(ret);
}
return( "" );
}
ConsoleFunctionGroupBegin( Platform , "General platform functions.");
ConsoleFunction( lockMouse, void, 2, 2, "(bool isLocked)"
"@brief Lock or unlock the mouse to the window.\n\n"
"When true, prevents the mouse from leaving the bounds of the game window.\n\n"
"@ingroup Input")
{
Platform::setWindowLocked(dAtob(argv[1]));
}
ConsoleFunction( setNetPort, bool, 2, 3, "(int port, bool bind=true)"
"@brief Set the network port for the game to use.\n\n"
"@param port The port to use.\n"
"@param bind True if bind() should be called on the port.\n"
"@returns True if the port was successfully opened.\n"
"This will trigger a windows firewall prompt. "
"If you don't have firewall tunneling tech you can set this to false to avoid the prompt.\n\n"
"@ingroup Networking")
{
bool bind = true;
if (argc == 3)
bind = dAtob(argv[2]);
return Net::openPort(dAtoi(argv[1]), bind);
}
ConsoleFunction( closeNetPort, void, 1, 1, "()"
"@brief Closes the current network port\n\n"
"@ingroup Networking")
{
Net::closePort();
}
ConsoleFunction( saveJournal, void, 2, 2, "(string filename)"
"Save the journal to the specified file.\n\n"
"@ingroup Platform")
{
Journal::Record(argv[1]);
}
ConsoleFunction( playJournal, void, 2, 3, "(string filename)"
"@brief Begin playback of a journal from a specified field.\n\n"
"@param filename Name and path of file journal file\n"
"@ingroup Platform")
{
// CodeReview - BJG 4/24/2007 - The break flag needs to be wired back in.
// bool jBreak = (argc > 2)? dAtob(argv[2]): false;
Journal::Play(argv[1]);
}
ConsoleFunction( getSimTime, S32, 1, 1, "()"
"Return the current sim time in milliseconds.\n\n"
"@brief Sim time is time since the game started.\n\n"
"@ingroup Platform")
{
return Sim::getCurrentTime();
}
ConsoleFunction( getRealTime, S32, 1, 1, "()"
"@brief Return the current real time in milliseconds.\n\n"
"Real time is platform defined; typically time since the computer booted.\n\n"
"@ingroup Platform")
{
return Platform::getRealMilliseconds();
}
ConsoleFunctionGroupEnd(Platform);
//-----------------------------------------------------------------------------
bool clientProcess(U32 timeDelta)
{
bool ret = true;
#ifndef TORQUE_TGB_ONLY
ret = ClientProcessList::get()->advanceTime(timeDelta);
#else
ret = gt2dNetworkClientProcess.advanceTime( timeDelta );
#endif
ITickable::advanceTime(timeDelta);
#ifndef TORQUE_TGB_ONLY
// Determine if we're lagging
GameConnection* connection = GameConnection::getConnectionToServer();
if(connection)
{
connection->detectLag();
}
#else
// Determine if we're lagging
t2dGameConnection* connection = t2dGameConnection::getConnectionToServer();
if(connection)
{
connection->detectLag();
}
#endif
// Let SFX process.
SFX->_update();
return ret;
}
bool serverProcess(U32 timeDelta)
{
bool ret = true;
#ifndef TORQUE_TGB_ONLY
ret = ServerProcessList::get()->advanceTime(timeDelta);
#else
ret = gt2dNetworkServerProcess.advanceTime( timeDelta );
#endif
return ret;
}

37
Engine/source/app/game.h Normal file
View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GAME_H_
#define _GAME_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
/// Processes the next frame, including gui, rendering, and tick interpolation.
/// This function will only have an effect when executed on the client.
bool clientProcess(U32 timeDelta);
/// Processes the next cycle on the server. This function will only have an effect when executed on the server.
bool serverProcess(U32 timeDelta);
#endif

View file

@ -0,0 +1,627 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "app/mainLoop.h"
#include "app/game.h"
#include "platform/platformTimer.h"
#include "platform/platformRedBook.h"
#include "platform/platformVolume.h"
#include "platform/platformMemory.h"
#include "platform/platformTimer.h"
#include "platform/platformNet.h"
#include "platform/nativeDialogs/fileDialog.h"
#include "platform/threads/thread.h"
#include "core/module.h"
#include "core/threadStatic.h"
#include "core/iTickable.h"
#include "core/stream/fileStream.h"
#include "windowManager/platformWindowMgr.h"
#include "core/util/journal/process.h"
#include "util/fpsTracker.h"
#include "console/debugOutputConsumer.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "gfx/bitmap/gBitmap.h"
#include "gfx/gFont.h"
#include "gfx/video/videoCapture.h"
#include "gfx/gfxTextureManager.h"
#include "sim/netStringTable.h"
#include "sim/actionMap.h"
#include "sim/netInterface.h"
#include "util/sampler.h"
#include "platform/threads/threadPool.h"
// For the TickMs define... fix this for T2D...
#include "T3D/gameBase/processList.h"
#ifdef TORQUE_DEMO_PURCHASE
#include "demo/pestTimer/pestTimer.h"
#endif
#ifdef TORQUE_ENABLE_VFS
#include "platform/platformVFS.h"
#endif
DITTS( F32, gTimeScale, 1.0 );
DITTS( U32, gTimeAdvance, 0 );
DITTS( U32, gFrameSkip, 0 );
extern S32 sgBackgroundProcessSleepTime;
extern S32 sgTimeManagerProcessInterval;
extern FPSTracker gFPS;
TimeManager* tm = NULL;
static bool gRequiresRestart = false;
#ifdef TORQUE_DEBUG
/// Temporary timer used to time startup times.
static PlatformTimer* gStartupTimer;
#endif
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
StringTableEntry gMiniDumpDir;
StringTableEntry gMiniDumpExec;
StringTableEntry gMiniDumpParams;
StringTableEntry gMiniDumpExecDir;
#endif
namespace engineAPI
{
// This is the magic switch for deciding which interop the engine
// should use. It will go away when we drop the console system
// entirely but for now it is necessary for several behaviors that
// differ between the interops to decide what to do.
bool gUseConsoleInterop = true;
bool gIsInitialized = false;
}
// The following are some tricks to make the memory leak checker run after global
// dtors have executed by placing some code in the termination segments.
#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER )
#ifdef TORQUE_COMPILER_VISUALC
# pragma data_seg( ".CRT$XTU" )
static void* sCheckMemBeforeTermination = &Memory::ensureAllFreed;
# pragma data_seg()
#elif defined( TORQUE_COMPILER_GCC )
__attribute__ ( ( destructor ) ) static void _ensureAllFreed()
{
Memory::ensureAllFreed();
}
#endif
#endif
// Process a time event and update all sub-processes
void processTimeEvent(S32 elapsedTime)
{
PROFILE_START(ProcessTimeEvent);
// If recording a video and not playinb back a journal, override the elapsedTime
if (VIDCAP->isRecording() && !Journal::IsPlaying())
elapsedTime = VIDCAP->getMsPerFrame();
// cap the elapsed time to one second
// if it's more than that we're probably in a bad catch-up situation
if(elapsedTime > 1024)
elapsedTime = 1024;
U32 timeDelta;
if(ATTS(gTimeAdvance))
timeDelta = ATTS(gTimeAdvance);
else
timeDelta = (U32) (elapsedTime * ATTS(gTimeScale));
Platform::advanceTime(elapsedTime);
// Don't build up more time than a single tick... this makes the sim
// frame rate dependent but is a useful hack for singleplayer.
if ( ATTS(gFrameSkip) )
if ( timeDelta > TickMs )
timeDelta = TickMs;
bool tickPass;
PROFILE_START(ServerProcess);
tickPass = serverProcess(timeDelta);
PROFILE_END();
PROFILE_START(ServerNetProcess);
// only send packets if a tick happened
if(tickPass)
GNet->processServer();
// Used to indicate if server was just ticked.
Con::setBoolVariable( "$pref::hasServerTicked", tickPass );
PROFILE_END();
PROFILE_START(SimAdvanceTime);
Sim::advanceTime(timeDelta);
PROFILE_END();
PROFILE_START(ClientProcess);
tickPass = clientProcess(timeDelta);
// Used to indicate if client was just ticked.
Con::setBoolVariable( "$pref::hasClientTicked", tickPass );
PROFILE_END_NAMED(ClientProcess);
PROFILE_START(ClientNetProcess);
if(tickPass)
GNet->processClient();
PROFILE_END();
GNet->checkTimeouts();
gFPS.update();
// Give the texture manager a chance to cleanup any
// textures that haven't been referenced for a bit.
if( GFX )
TEXMGR->cleanupCache( 5 );
PROFILE_END();
// Update the console time
Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000);
}
void StandardMainLoop::init()
{
#ifdef TORQUE_DEBUG
gStartupTimer = PlatformTimer::create();
#endif
#ifdef TORQUE_DEBUG_GUARD
Memory::flagCurrentAllocs( Memory::FLAG_Global );
#endif
Platform::setMathControlStateKnown();
// Asserts should be created FIRST
PlatformAssert::create();
ManagedSingleton< ThreadManager >::createSingleton();
FrameAllocator::init(TORQUE_FRAME_SIZE); // See comments in torqueConfig.h
// Yell if we can't initialize the network.
if(!Net::init())
{
AssertISV(false, "StandardMainLoop::initCore - could not initialize networking!");
}
_StringTable::create();
// Set up the resource manager and get some basic file types in it.
Con::init();
Platform::initConsole();
NetStringTable::create();
// Use debug output logging on the Xbox and OSX builds
#if defined( _XBOX ) || defined( TORQUE_OS_MAC )
DebugOutputConsumer::init();
#endif
Processor::init();
Math::init();
Platform::init(); // platform specific initialization
RedBook::init();
Platform::initConsole();
ThreadPool::GlobalThreadPool::createSingleton();
// Initialize modules.
ModuleManager::initializeSystem();
// Initialise ITickable.
#ifdef TORQUE_TGB_ONLY
ITickable::init( 4 );
#endif
#ifdef TORQUE_ENABLE_VFS
// [tom, 10/28/2006] Load the VFS here so that it stays loaded
Zip::ZipArchive *vfs = openEmbeddedVFSArchive();
gResourceManager->addVFSRoot(vfs);
#endif
Con::addVariable("timeScale", TypeF32, &ATTS(gTimeScale), "Animation time scale.\n"
"@ingroup platform");
Con::addVariable("timeAdvance", TypeS32, &ATTS(gTimeAdvance), "The speed at which system processing time advances.\n"
"@ingroup platform");
Con::addVariable("frameSkip", TypeS32, &ATTS(gFrameSkip), "Sets the number of frames to skip while rendering the scene.\n"
"@ingroup platform");
Con::setVariable( "defaultGame", StringTable->insert("scripts") );
Con::addVariable( "_forceAllMainThread", TypeBool, &ThreadPool::getForceAllMainThread(), "Force all work items to execute on main thread. turns this into a single-threaded system. Primarily useful to find whether malfunctions are caused by parallel execution or not.\n"
"@ingroup platform" );
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
Con::addVariable("MiniDump::Dir", TypeString, &gMiniDumpDir);
Con::addVariable("MiniDump::Exec", TypeString, &gMiniDumpExec);
Con::addVariable("MiniDump::Params", TypeString, &gMiniDumpParams);
Con::addVariable("MiniDump::ExecDir", TypeString, &gMiniDumpExecDir);
#endif
ActionMap* globalMap = new ActionMap;
globalMap->registerObject("GlobalActionMap");
Sim::getActiveActionMapSet()->pushObject(globalMap);
// Do this before we init the process so that process notifiees can get the time manager
tm = new TimeManager;
tm->timeEvent.notify(&::processTimeEvent);
Sampler::init();
// Hook in for UDP notification
Net::smPacketReceive.notify(GNet, &NetInterface::processPacketReceiveEvent);
#ifdef TORQUE_DEMO_PURCHASE
PestTimerinit();
#endif
#ifdef TORQUE_DEBUG_GUARD
Memory::flagCurrentAllocs( Memory::FLAG_Static );
#endif
}
void StandardMainLoop::shutdown()
{
delete tm;
preShutdown();
// Shut down modules.
ModuleManager::shutdownSystem();
ThreadPool::GlobalThreadPool::deleteSingleton();
#ifdef TORQUE_ENABLE_VFS
closeEmbeddedVFSArchive();
#endif
RedBook::destroy();
Platform::shutdown();
#if defined( _XBOX ) || defined( TORQUE_OS_MAC )
DebugOutputConsumer::destroy();
#endif
NetStringTable::destroy();
Con::shutdown();
_StringTable::destroy();
FrameAllocator::destroy();
Net::shutdown();
Sampler::destroy();
ManagedSingleton< ThreadManager >::deleteSingleton();
// asserts should be destroyed LAST
PlatformAssert::destroy();
#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER )
Memory::validate();
#endif
}
void StandardMainLoop::preShutdown()
{
#ifdef TORQUE_TOOLS
// Tools are given a chance to do pre-quit processing
// - This is because for tools we like to do things such
// as prompting to save changes before shutting down
// and onExit is packaged which means we can't be sure
// where in the shutdown namespace chain we are when using
// onExit since some components of the tools may already be
// destroyed that may be vital to saving changes to avoid
// loss of work [1/5/2007 justind]
if( Con::isFunction("onPreExit") )
Con::executef( "onPreExit");
#endif
//exec the script onExit() function
if ( Con::isFunction( "onExit" ) )
Con::executef("onExit");
}
bool StandardMainLoop::handleCommandLine( S32 argc, const char **argv )
{
// Allow the window manager to process command line inputs; this is
// done to let web plugin functionality happen in a fairly transparent way.
PlatformWindowManager::get()->processCmdLineArgs(argc, argv);
Process::handleCommandLine( argc, argv );
// Set up the command line args for the console scripts...
Con::setIntVariable("Game::argc", argc);
U32 i;
for (i = 0; i < argc; i++)
Con::setVariable(avar("Game::argv%d", i), argv[i]);
Platform::FS::InstallFileSystems(); // install all drives for now until we have everything using the volume stuff
Platform::FS::MountDefaults();
// Set our working directory.
Torque::FS::SetCwd( "game:/" );
// Set our working directory.
Platform::setCurrentDirectory( Platform::getMainDotCsDir() );
#ifdef TORQUE_PLAYER
if(argc > 2 && dStricmp(argv[1], "-project") == 0)
{
char playerPath[1024];
Platform::makeFullPathName(argv[2], playerPath, sizeof(playerPath));
Platform::setCurrentDirectory(playerPath);
argv += 2;
argc -= 2;
// Re-locate the game:/ asset mount.
Torque::FS::Unmount( "game" );
Torque::FS::Mount( "game", Platform::FS::createNativeFS( playerPath ) );
}
#endif
// Executes an entry script file. This is "main.cs"
// by default, but any file name (with no whitespace
// in it) may be run if it is specified as the first
// command-line parameter. The script used, default
// or otherwise, is not compiled and is loaded here
// directly because the resource system restricts
// access to the "root" directory.
#ifdef TORQUE_ENABLE_VFS
Zip::ZipArchive *vfs = openEmbeddedVFSArchive();
bool useVFS = vfs != NULL;
#endif
Stream *mainCsStream = NULL;
// The working filestream.
FileStream str;
const char *defaultScriptName = "main.cs";
bool useDefaultScript = true;
// Check if any command-line parameters were passed (the first is just the app name).
if (argc > 1)
{
// If so, check if the first parameter is a file to open.
if ( (dStrcmp(argv[1], "") != 0 ) && (str.open(argv[1], Torque::FS::File::Read)) )
{
// If it opens, we assume it is the script to run.
useDefaultScript = false;
#ifdef TORQUE_ENABLE_VFS
useVFS = false;
#endif
mainCsStream = &str;
}
}
if (useDefaultScript)
{
bool success = false;
#ifdef TORQUE_ENABLE_VFS
if(useVFS)
success = (mainCsStream = vfs->openFile(defaultScriptName, Zip::ZipArchive::Read)) != NULL;
else
#endif
success = str.open(defaultScriptName, Torque::FS::File::Read);
#if defined( TORQUE_DEBUG ) && defined (TORQUE_TOOLS) && !defined( _XBOX )
if (!success)
{
OpenFileDialog ofd;
FileDialogData &fdd = ofd.getData();
fdd.mFilters = StringTable->insert("Main Entry Script (main.cs)|main.cs|");
fdd.mTitle = StringTable->insert("Locate Game Entry Script");
// Get the user's selection
if( !ofd.Execute() )
return false;
// Process and update CWD so we can run the selected main.cs
S32 pathLen = dStrlen( fdd.mFile );
FrameTemp<char> szPathCopy( pathLen + 1);
dStrcpy( szPathCopy, fdd.mFile );
//forwardslash( szPathCopy );
const char *path = dStrrchr(szPathCopy, '/');
if(path)
{
U32 len = path - (const char*)szPathCopy;
szPathCopy[len+1] = 0;
Platform::setCurrentDirectory(szPathCopy);
// Re-locate the game:/ asset mount.
Torque::FS::Unmount( "game" );
Torque::FS::Mount( "game", Platform::FS::createNativeFS( ( const char* ) szPathCopy ) );
success = str.open(fdd.mFile, Torque::FS::File::Read);
if(success)
defaultScriptName = fdd.mFile;
}
}
#endif
if( !success )
{
char msg[1024];
dSprintf(msg, sizeof(msg), "Failed to open \"%s\".", defaultScriptName);
Platform::AlertOK("Error", msg);
#ifdef TORQUE_ENABLE_VFS
closeEmbeddedVFSArchive();
#endif
return false;
}
#ifdef TORQUE_ENABLE_VFS
if(! useVFS)
#endif
mainCsStream = &str;
}
// This should rarely happen, but lets deal with
// it gracefully if it does.
if ( mainCsStream == NULL )
return false;
U32 size = mainCsStream->getStreamSize();
char *script = new char[size + 1];
mainCsStream->read(size, script);
#ifdef TORQUE_ENABLE_VFS
if(useVFS)
vfs->closeFile(mainCsStream);
else
#endif
str.close();
script[size] = 0;
char buffer[1024], *ptr;
Platform::makeFullPathName(useDefaultScript ? defaultScriptName : argv[1], buffer, sizeof(buffer), Platform::getCurrentDirectory());
ptr = dStrrchr(buffer, '/');
if(ptr != NULL)
*ptr = 0;
Platform::setMainDotCsDir(buffer);
Platform::setCurrentDirectory(buffer);
Con::evaluate(script, false, useDefaultScript ? defaultScriptName : argv[1]);
delete[] script;
#ifdef TORQUE_ENABLE_VFS
closeEmbeddedVFSArchive();
#endif
return true;
}
bool StandardMainLoop::doMainLoop()
{
#ifdef TORQUE_DEBUG
if( gStartupTimer )
{
Con::printf( "Started up in %.2f seconds...",
F32( gStartupTimer->getElapsedMs() ) / 1000.f );
SAFE_DELETE( gStartupTimer );
}
#endif
bool keepRunning = true;
// while(keepRunning)
{
tm->setBackgroundThreshold(mClamp(sgBackgroundProcessSleepTime, 1, 200));
tm->setForegroundThreshold(mClamp(sgTimeManagerProcessInterval, 1, 200));
// update foreground/background status
if(WindowManager->getFirstWindow())
{
static bool lastFocus = false;
bool newFocus = ( WindowManager->getFocusedWindow() != NULL );
if(lastFocus != newFocus)
{
#ifndef TORQUE_SHIPPING
Con::printf("Window focus status changed: focus: %d", newFocus);
if (!newFocus)
Con::printf(" Using background sleep time: %u", Platform::getBackgroundSleepTime());
#endif
#ifdef TORQUE_OS_MAC
if (newFocus)
WindowManager->getFirstWindow()->show();
#endif
lastFocus = newFocus;
}
#ifndef TORQUE_OS_MAC
// under the web plugin do not sleep the process when the child window loses focus as this will cripple the browser perfomance
if (!Platform::getWebDeployment())
tm->setBackground(!newFocus);
else
tm->setBackground(false);
#else
tm->setBackground(false);
#endif
}
else
{
tm->setBackground(false);
}
PROFILE_START(MainLoop);
Sampler::beginFrame();
if(!Process::processEvents())
keepRunning = false;
ThreadPool::processMainThreadWorkItems();
Sampler::endFrame();
PROFILE_END_NAMED(MainLoop);
#ifdef TORQUE_DEMO_PURCHASE
CheckTimer();
CheckBlocker();
#endif
}
return keepRunning;
}
void StandardMainLoop::setRestart(bool restart )
{
gRequiresRestart = restart;
}
bool StandardMainLoop::requiresRestart()
{
return gRequiresRestart;
}

View file

@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _APP_MAINLOOP_H_
#define _APP_MAINLOOP_H_
#include "platform/platform.h"
/// Support class to simplify the process of writing a main loop for Torque apps.
class StandardMainLoop
{
public:
/// Initialize core libraries and call registered init functions
static void init();
/// Pass command line arguments to registered functions and main.cs
static bool handleCommandLine(S32 argc, const char **argv);
/// A standard mainloop implementation.
static bool doMainLoop();
/// Shut down the core libraries and call registered shutdown fucntions.
static void shutdown();
static void setRestart( bool restart );
static bool requiresRestart();
private:
/// Handle "pre shutdown" tasks like notifying scripts BEFORE we delete
/// stuff from under them.
static void preShutdown();
};
#endif

View file

@ -0,0 +1,455 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "app/net/httpObject.h"
#include "platform/platform.h"
#include "platform/event.h"
#include "core/stream/fileStream.h"
#include "console/simBase.h"
#include "console/consoleInternal.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(HTTPObject);
ConsoleDocClass( HTTPObject,
"@brief Allows communications between the game and a server using HTTP.\n\n"
"HTTPObject is derrived from TCPObject and makes use of the same callbacks for dealing with "
"connections and received data. However, the way in which you use HTTPObject to connect "
"with a server is different than TCPObject. Rather than opening a connection, sending data, "
"waiting to receive data, and then closing the connection, you issue a get() or post() and "
"handle the response. The connection is automatically created and destroyed for you.\n\n"
"@tsexample\n"
"// In this example we'll retrieve the weather in Las Vegas using\n"
"// Google's API. The response is in XML which could be processed\n"
"// and used by the game using SimXMLDocument, but we'll just output\n"
"// the results to the console in this example.\n\n"
"// Define callbacks for our specific HTTPObject using our instance's\n"
"// name (WeatherFeed) as the namespace.\n\n"
"// Handle an issue with resolving the server's name\n"
"function WeatherFeed::onDNSFailed(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"DNSFailed\";\n\n"
" // Handle DNS failure\n"
"}\n\n"
"function WeatherFeed::onConnectFailed(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"ConnectFailed\";\n\n"
" // Handle connection failure\n"
"}\n\n"
"function WeatherFeed::onDNSResolved(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"DNSResolved\";\n\n"
"}\n\n"
"function WeatherFeed::onConnected(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"Connected\";\n\n"
" // Clear our buffer\n"
" %this.buffer = \"\";\n"
"}\n\n"
"function WeatherFeed::onDisconnect(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"Disconnected\";\n\n"
" // Output the buffer to the console\n"
" echo(\"Google Weather Results:\");\n"
" echo(%this.buffer);\n"
"}\n\n"
"// Handle a line from the server\n"
"function WeatherFeed::onLine(%this, %line)\n"
"{\n"
" // Store this line in out buffer\n"
" %this.buffer = %this.buffer @ %line;\n"
"}\n\n"
"// Create the HTTPObject\n"
"%feed = new HTTPObject(WeatherFeed);\n\n"
"// Define a dynamic field to store the last connection state\n"
"%feed.lastState = \"None\";\n\n"
"// Send the GET command\n"
"%feed.get(\"www.google.com:80\", \"/ig/api\", \"weather=Las-Vegas,US\");\n"
"@endtsexample\n\n"
"@see TCPObject\n"
"@ingroup Networking\n"
);
//--------------------------------------
HTTPObject::HTTPObject()
{
mHostName = 0;
mPath = 0;
mQuery = 0;
mPost = 0;
mBufferSave = 0;
}
HTTPObject::~HTTPObject()
{
dFree(mHostName);
dFree(mPath);
dFree(mQuery);
dFree(mPost);
mHostName = 0;
mPath = 0;
mQuery = 0;
mPost = 0;
dFree(mBufferSave);
}
//--------------------------------------
//--------------------------------------
void HTTPObject::get(const char *host, const char *path, const char *query)
{
if(mHostName)
dFree(mHostName);
if(mPath)
dFree(mPath);
if(mQuery)
dFree(mQuery);
if(mPost)
dFree(mPost);
if(mBufferSave)
dFree(mBufferSave);
mBufferSave = 0;
mHostName = dStrdup(host);
mPath = dStrdup(path);
if(query)
mQuery = dStrdup(query);
else
mQuery = NULL;
mPost = NULL;
connect(host);
}
void HTTPObject::post(const char *host, const char *path, const char *query, const char *post)
{
if(mHostName)
dFree(mHostName);
if(mPath)
dFree(mPath);
if(mQuery)
dFree(mQuery);
if(mPost)
dFree(mPost);
if(mBufferSave)
dFree(mBufferSave);
mBufferSave = 0;
mHostName = dStrdup(host);
mPath = dStrdup(path);
if(query && query[0])
mQuery = dStrdup(query);
else
mQuery = NULL;
mPost = dStrdup(post);
connect(host);
}
static char getHex(char c)
{
if(c <= 9)
return c + '0';
return c - 10 + 'A';
}
static S32 getHexVal(char c)
{
if(c >= '0' && c <= '9')
return c - '0';
else if(c >= 'A' && c <= 'Z')
return c - 'A' + 10;
else if(c >= 'a' && c <= 'z')
return c - 'a' + 10;
return -1;
}
void HTTPObject::expandPath(char *dest, const char *path, U32 destSize)
{
static bool asciiEscapeTableBuilt = false;
static bool asciiEscapeTable[256];
if(!asciiEscapeTableBuilt)
{
asciiEscapeTableBuilt = true;
U32 i;
for(i = 0; i <= ' '; i++)
asciiEscapeTable[i] = true;
for(;i <= 0x7F; i++)
asciiEscapeTable[i] = false;
for(;i <= 0xFF; i++)
asciiEscapeTable[i] = true;
asciiEscapeTable[static_cast<U32>('\"')] = true;
asciiEscapeTable[static_cast<U32>('_')] = true;
asciiEscapeTable[static_cast<U32>('\'')] = true;
asciiEscapeTable[static_cast<U32>('#')] = true;
asciiEscapeTable[static_cast<U32>('$')] = true;
asciiEscapeTable[static_cast<U32>('%')] = true;
asciiEscapeTable[static_cast<U32>('&')] = false;
asciiEscapeTable[static_cast<U32>('+')] = true;
asciiEscapeTable[static_cast<U32>('-')] = true;
asciiEscapeTable[static_cast<U32>('~')] = true;
}
U32 destIndex = 0;
U32 srcIndex = 0;
while(path[srcIndex] && destIndex < destSize - 3)
{
char c = path[srcIndex++];
if(asciiEscapeTable[static_cast<U32>(c)])
{
dest[destIndex++] = '%';
dest[destIndex++] = getHex((c >> 4) & 0xF);
dest[destIndex++] = getHex(c & 0xF);
}
else
dest[destIndex++] = c;
}
dest[destIndex] = 0;
}
//--------------------------------------
void HTTPObject::onConnected()
{
Parent::onConnected();
char expPath[8192];
char buffer[8192];
if(mQuery)
{
dSprintf(buffer, sizeof(buffer), "%s?%s", mPath, mQuery);
expandPath(expPath, buffer, sizeof(expPath));
}
else
expandPath(expPath, mPath, sizeof(expPath));
char *pt = dStrchr(mHostName, ':');
if(pt)
*pt = 0;
dSprintf(buffer, sizeof(buffer), "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", expPath, mHostName);
if(pt)
*pt = ':';
send((U8*)buffer, dStrlen(buffer));
mParseState = ParsingStatusLine;
mChunkedEncoding = false;
}
void HTTPObject::onConnectFailed()
{
dFree(mHostName);
dFree(mPath);
dFree(mQuery);
mHostName = 0;
mPath = 0;
mQuery = 0;
Parent::onConnectFailed();
}
void HTTPObject::onDisconnect()
{
dFree(mHostName);
dFree(mPath);
dFree(mQuery);
mHostName = 0;
mPath = 0;
mQuery = 0;
Parent::onDisconnect();
}
bool HTTPObject::processLine(U8 *line)
{
if(mParseState == ParsingStatusLine)
{
mParseState = ParsingHeader;
}
else if(mParseState == ParsingHeader)
{
if(!dStricmp((char *) line, "transfer-encoding: chunked"))
mChunkedEncoding = true;
if(line[0] == 0)
{
if(mChunkedEncoding)
mParseState = ParsingChunkHeader;
else
mParseState = ProcessingBody;
return true;
}
}
else if(mParseState == ParsingChunkHeader)
{
if(line[0]) // strip off the crlf if necessary
{
mChunkSize = 0;
S32 hexVal;
while((hexVal = getHexVal(*line++)) != -1)
{
mChunkSize *= 16;
mChunkSize += hexVal;
}
if(mBufferSave)
{
mBuffer = mBufferSave;
mBufferSize = mBufferSaveSize;
mBufferSave = 0;
}
if(mChunkSize)
mParseState = ProcessingBody;
else
{
mParseState = ProcessingDone;
finishLastLine();
}
}
}
else
{
return Parent::processLine((UTF8*)line);
}
return true;
}
U32 HTTPObject::onDataReceive(U8 *buffer, U32 bufferLen)
{
U32 start = 0;
parseLine(buffer, &start, bufferLen);
return start;
}
//--------------------------------------
U32 HTTPObject::onReceive(U8 *buffer, U32 bufferLen)
{
if(mParseState == ProcessingBody)
{
if(mChunkedEncoding && bufferLen >= mChunkSize)
{
U32 ret = onDataReceive(buffer, mChunkSize);
mChunkSize -= ret;
if(mChunkSize == 0)
{
if(mBuffer)
{
mBufferSaveSize = mBufferSize;
mBufferSave = mBuffer;
mBuffer = 0;
mBufferSize = 0;
}
mParseState = ParsingChunkHeader;
}
return ret;
}
else
{
U32 ret = onDataReceive(buffer, bufferLen);
mChunkSize -= ret;
return ret;
}
}
else if(mParseState != ProcessingDone)
{
U32 start = 0;
parseLine(buffer, &start, bufferLen);
return start;
}
return bufferLen;
}
//--------------------------------------
DefineEngineMethod( HTTPObject, get, void, ( const char* Address, const char* requirstURI, const char* query ), ( "" ),
"@brief Send a GET command to a server to send or retrieve data.\n\n"
"@param Address HTTP web address to send this get call to. Be sure to include the port at the end (IE: \"www.garagegames.com:80\").\n"
"@param requirstURI Specific location on the server to access (IE: \"index.php\".)\n"
"@param query Optional. Actual data to transmit to the server. Can be anything required providing it sticks with limitations of the HTTP protocol. "
"If you were building the URL manually, this is the text that follows the question mark. For example: http://www.google.com/ig/api?<b>weather=Las-Vegas,US</b>\n"
"@tsexample\n"
"// Create an HTTP object for communications\n"
"%httpObj = new HTTPObject();\n\n"
"// Specify a URL to transmit to\n"
"%url = \"www.garagegames.com:80\";\n\n"
"// Specify a URI to communicate with\n"
"%URI = \"/index.php\";\n\n"
"// Specify a query to send.\n"
"%query = \"\";\n\n"
"// Send the GET command to the server\n"
"%httpObj.get(%url,%URI,%query);\n"
"@endtsexample\n\n"
)
{
if( !query || !query[ 0 ] )
object->get(Address, requirstURI, NULL);
else
object->get(Address, requirstURI, query);
}
DefineEngineMethod( HTTPObject, post, void, ( const char* Address, const char* requirstURI, const char* query, const char* post ),,
"@brief Send POST command to a server to send or retrieve data.\n\n"
"@param Address HTTP web address to send this get call to. Be sure to include the port at the end (IE: \"www.garagegames.com:80\").\n"
"@param requirstURI Specific location on the server to access (IE: \"index.php\".)\n"
"@param query Actual data to transmit to the server. Can be anything required providing it sticks with limitations of the HTTP protocol. \n"
"@param post Submission data to be processed.\n"
"@note The post() method is currently non-functional.\n"
"@tsexample\n"
"// Create an HTTP object for communications\n"
"%httpObj = new HTTPObject();\n\n"
"// Specify a URL to transmit to\n"
"%url = \"www.garagegames.com:80\";\n\n"
"// Specify a URI to communicate with\n"
"%URI = \"/index.php\";\n\n"
"// Specify a query to send.\n"
"%query = \"\";\n\n"
"// Specify the submission data.\n"
"%post = \"\";\n\n"
"// Send the POST command to the server\n"
"%httpObj.POST(%url,%URI,%query,%post);\n"
"@endtsexample\n\n"
)
{
object->post(Address, requirstURI, query, post);
}

View file

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _HTTPOBJECT_H_
#define _HTTPOBJECT_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _TCPOBJECT_H_
#include "app/net/tcpObject.h"
#endif
class HTTPObject : public TCPObject
{
private:
typedef TCPObject Parent;
protected:
enum ParseState {
ParsingStatusLine,
ParsingHeader,
ParsingChunkHeader,
ProcessingBody,
ProcessingDone,
};
ParseState mParseState;
U32 mTotalBytes;
U32 mBytesRemaining;
public:
U32 mStatus;
F32 mVersion;
U32 mContentLength;
bool mChunkedEncoding;
U32 mChunkSize;
const char *mContentType;
char *mHostName;
char *mPath;
char *mQuery;
char *mPost;
U8 *mBufferSave;
U32 mBufferSaveSize;
public:
static void expandPath(char *dest, const char *path, U32 destSize);
void get(const char *hostName, const char *urlName, const char *query);
void post(const char *host, const char *path, const char *query, const char *post);
HTTPObject();
~HTTPObject();
//static HTTPObject *find(U32 tag);
virtual U32 onDataReceive(U8 *buffer, U32 bufferLen);
virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data
virtual void onConnected();
virtual void onConnectFailed();
virtual void onDisconnect();
bool processLine(U8 *line);
DECLARE_CONOBJECT(HTTPObject);
};
#endif // _H_HTTPOBJECT_

View file

@ -0,0 +1,417 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/dnet.h"
#include "core/idGenerator.h"
#include "core/stream/bitStream.h"
#include "console/simBase.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "sim/netConnection.h"
#include "sim/netObject.h"
#include "app/net/serverQuery.h"
#include "console/engineAPI.h"
//----------------------------------------------------------------
// remote procedure call console functions
//----------------------------------------------------------------
class RemoteCommandEvent : public NetEvent
{
public:
typedef NetEvent Parent;
enum {
MaxRemoteCommandArgs = 20,
CommandArgsBits = 5
};
private:
S32 mArgc;
char *mArgv[MaxRemoteCommandArgs + 1];
NetStringHandle mTagv[MaxRemoteCommandArgs + 1];
static char mBuf[1024];
public:
RemoteCommandEvent(S32 argc=0, const char **argv=NULL, NetConnection *conn = NULL)
{
mArgc = argc;
for(S32 i = 0; i < argc; i++)
{
if(argv[i][0] == StringTagPrefixByte)
{
char buffer[256];
mTagv[i+1] = NetStringHandle(dAtoi(argv[i]+1));
if(conn)
{
dSprintf(buffer + 1, sizeof(buffer) - 1, "%d", conn->getNetSendId(mTagv[i+1]));
buffer[0] = StringTagPrefixByte;
mArgv[i+1] = dStrdup(buffer);
}
}
else
mArgv[i+1] = dStrdup(argv[i]);
}
}
#ifdef TORQUE_DEBUG_NET
const char *getDebugName()
{
static char buffer[256];
dSprintf(buffer, sizeof(buffer), "%s [%s]", getClassName(), mTagv[1].isValidString() ? mTagv[1].getString() : "--unknown--" );
return buffer;
}
#endif
~RemoteCommandEvent()
{
for(S32 i = 0; i < mArgc; i++)
dFree(mArgv[i+1]);
}
virtual void pack(NetConnection* conn, BitStream *bstream)
{
bstream->writeInt(mArgc, CommandArgsBits);
// write it out reversed... why?
// automatic string substitution with later arguments -
// handled automatically by the system.
for(S32 i = 0; i < mArgc; i++)
conn->packString(bstream, mArgv[i+1]);
}
virtual void write(NetConnection* conn, BitStream *bstream)
{
pack(conn, bstream);
}
virtual void unpack(NetConnection* conn, BitStream *bstream)
{
mArgc = bstream->readInt(CommandArgsBits);
// read it out backwards
for(S32 i = 0; i < mArgc; i++)
{
conn->unpackString(bstream, mBuf);
mArgv[i+1] = dStrdup(mBuf);
}
}
virtual void process(NetConnection *conn)
{
static char idBuf[10];
// de-tag the command name
for(S32 i = mArgc - 1; i >= 0; i--)
{
char *arg = mArgv[i+1];
if(*arg == StringTagPrefixByte)
{
// it's a tag:
U32 localTag = dAtoi(arg + 1);
NetStringHandle tag = conn->translateRemoteStringId(localTag);
NetStringTable::expandString( tag,
mBuf,
sizeof(mBuf),
(mArgc - 1) - i,
(const char**)(mArgv + i + 2) );
dFree(mArgv[i+1]);
mArgv[i+1] = dStrdup(mBuf);
}
}
const char *rmtCommandName = dStrchr(mArgv[1], ' ') + 1;
if(conn->isConnectionToServer())
{
dStrcpy(mBuf, "clientCmd");
dStrcat(mBuf, rmtCommandName);
char *temp = mArgv[1];
mArgv[1] = mBuf;
Con::execute(mArgc, (const char **) mArgv+1);
mArgv[1] = temp;
}
else
{
dStrcpy(mBuf, "serverCmd");
dStrcat(mBuf, rmtCommandName);
char *temp = mArgv[1];
dSprintf(idBuf, sizeof(idBuf), "%d", conn->getId());
mArgv[0] = mBuf;
mArgv[1] = idBuf;
Con::execute(mArgc+1, (const char **) mArgv);
mArgv[1] = temp;
}
}
DECLARE_CONOBJECT(RemoteCommandEvent);
};
char RemoteCommandEvent::mBuf[1024];
IMPLEMENT_CO_NETEVENT_V1(RemoteCommandEvent);
ConsoleDocClass( RemoteCommandEvent,
"@brief Object used for remote procedure calls.\n\n"
"Not intended for game development, for exposing ConsoleFunctions (such as commandToClient) only.\n\n"
"@internal");
static void sendRemoteCommand(NetConnection *conn, S32 argc, const char **argv)
{
if(U8(argv[0][0]) != StringTagPrefixByte)
{
Con::errorf(ConsoleLogEntry::Script, "Remote Command Error - command must be a tag.");
return;
}
S32 i;
for(i = argc - 1; i >= 0; i--)
{
if(argv[i][0] != 0)
break;
argc = i;
}
for(i = 0; i < argc; i++)
conn->validateSendString(argv[i]);
RemoteCommandEvent *cevt = new RemoteCommandEvent(argc, argv, conn);
conn->postNetEvent(cevt);
}
ConsoleFunctionGroupBegin( Net, "Functions for use with the network; tagged strings and remote commands.");
ConsoleFunction( commandToServer, void, 2, RemoteCommandEvent::MaxRemoteCommandArgs + 1, "(string func, ...)"
"@brief Send a command to the server.\n\n"
"@param func Name of the server command being called\n"
"@param ... Various parameters being passed to server command\n\n"
"@tsexample\n"
"// Create a standard function. Needs to be executed on the client, such \n"
"// as within scripts/client/default.bind.cs\n"
"function toggleCamera(%val)\n"
"{\n"
" // If key was down, call a server command named 'ToggleCamera'\n"
" if (%val)\n"
" commandToServer('ToggleCamera');\n"
"}\n\n"
"// Server command being called from above. Needs to be executed on the \n"
"// server, such as within scripts/server/commands.cs\n"
"function serverCmdToggleCamera(%client)\n"
"{\n"
" if (%client.getControlObject() == %client.player)\n"
" {\n"
" %client.camera.setVelocity(\"0 0 0\");\n"
" %control = %client.camera;\n"
" }\n"
" else\n"
" {\n"
" %client.player.setVelocity(\"0 0 0\");\n"
" %control = %client.player;\n"
" }\n"
" %client.setControlObject(%control);\n"
" clientCmdSyncEditorGui();\n"
"}\n"
"@endtsexample\n\n"
"@ingroup Networking")
{
NetConnection *conn = NetConnection::getConnectionToServer();
if(!conn)
return;
sendRemoteCommand(conn, argc - 1, argv + 1);
}
ConsoleFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(NetConnection client, string func, ...)"
"@brief Send a command from the server to the client\n\n"
"@param client The numeric ID of a client GameConnection\n"
"@param func Name of the client function being called\n"
"@param ... Various parameters being passed to client command\n\n"
"@tsexample\n"
"// Set up the client command. Needs to be executed on the client, such as\n"
"// within scripts/client/client.cs\n"
"// Update the Ammo Counter with current ammo, if not any then hide the counter.\n"
"function clientCmdSetAmmoAmountHud(%amount)\n"
"{\n"
" if (!%amount)\n"
" AmmoAmount.setVisible(false);\n"
" else\n"
" {\n"
" AmmoAmount.setVisible(true);\n"
" AmmoAmount.setText(\"Ammo: \"@%amount);\n"
" }\n"
"}\n\n"
"// Call it from a server function. Needs to be executed on the server, \n"
"//such as within scripts/server/game.cs\n"
"function GameConnection::setAmmoAmountHud(%client, %amount)\n"
"{\n"
" commandToClient(%client, 'SetAmmoAmountHud', %amount);\n"
"}\n"
"@endtsexample\n\n"
"@ingroup Networking\n")
{
NetConnection *conn;
if(!Sim::findObject(argv[1], conn))
return;
sendRemoteCommand(conn, argc - 2, argv + 2);
}
ConsoleFunction(removeTaggedString, void, 2, 2, "(int tag)"
"@brief Remove a tagged string from the Net String Table\n\n"
"@param tag The tag associated with the string\n\n"
"@see \\ref syntaxDataTypes under Tagged %Strings\n"
"@see addTaggedString()\n"
"@see getTaggedString()\n"
"@ingroup Networking\n")
{
gNetStringTable->removeString(dAtoi(argv[1]+1), true);
}
ConsoleFunction( addTaggedString, const char*, 2, 2, "(string str)"
"@brief Use the addTaggedString function to tag a new string and add it to the NetStringTable\n\n"
"@param str The string to be tagged and placed in the NetStringTable. Tagging ignores case, "
"so tagging the same string (excluding case differences) will be ignored as a duplicated tag.\n\n"
"@return Returns a string( containing a numeric value) equivalent to the string ID for the newly tagged string"
"@see \\ref syntaxDataTypes under Tagged %Strings\n"
"@see removeTaggedString()\n"
"@see getTaggedString()\n"
"@ingroup Networking\n")
{
NetStringHandle s(argv[1]);
gNetStringTable->incStringRefScript(s.getIndex());
char *ret = Con::getReturnBuffer(10);
ret[0] = StringTagPrefixByte;
dSprintf(ret + 1, 9, "%d", s.getIndex());
return ret;
}
ConsoleFunction( getTaggedString, const char*, 2, 2, "(int tag)"
"@brief Use the getTaggedString function to convert a tag to a string.\n\n"
"This is not the same as detag() which can only be used within the context "
"of a function that receives a tag. This function can be used any time and "
"anywhere to convert a tag to a string.\n\n"
"@param tag A numeric tag ID.\n"
"@returns The string as found in the Net String table.\n"
"@see \\ref syntaxDataTypes under Tagged %Strings\n"
"@see addTaggedString()\n"
"@see removeTaggedString()\n"
"@ingroup Networking\n")
{
const char *indexPtr = argv[1];
if (*indexPtr == StringTagPrefixByte)
indexPtr++;
return gNetStringTable->lookupString(dAtoi(indexPtr));
}
ConsoleFunction( buildTaggedString, const char*, 2, 11, "(string format, ...)"
"@brief Build a string using the specified tagged string format.\n\n"
"This function takes an already tagged string (passed in as a tagged string ID) and one "
"or more additional strings. If the tagged string contains argument tags that range from "
"%%1 through %%9, then each additional string will be substituted into the tagged string. "
"The final (non-tagged) combined string will be returned. The maximum length of the tagged "
"string plus any inserted additional strings is 511 characters.\n\n"
"@param format A tagged string ID that contains zero or more argument tags, in the form of "
"%%1 through %%9.\n"
"@param ... A variable number of arguments that are insterted into the tagged string "
"based on the argument tags within the format string.\n"
"@returns An ordinary string that is a combination of the original tagged string with any additional "
"strings passed in inserted in place of each argument tag.\n"
"@tsexample\n"
"// Create a tagged string with argument tags\n"
"%taggedStringID = addTaggedString(\"Welcome %1 to the game!\");\n\n"
"// Some point later, combine the tagged string with some other string\n"
"%string = buildTaggedString(%taggedStringID, %playerName);\n"
"echo(%string);\n"
"@endtsexample\n\n"
"@see \\ref syntaxDataTypes under Tagged %Strings\n"
"@see addTaggedString()\n"
"@see getTaggedString()\n"
"@ingroup Networking\n")
{
const char *indexPtr = argv[1];
if (*indexPtr == StringTagPrefixByte)
indexPtr++;
const char *fmtString = gNetStringTable->lookupString(dAtoi(indexPtr));
char *strBuffer = Con::getReturnBuffer(512);
const char *fmtStrPtr = fmtString;
char *strBufPtr = strBuffer;
S32 strMaxLength = 511;
if (!fmtString)
goto done;
//build the string
while (*fmtStrPtr)
{
//look for an argument tag
if (*fmtStrPtr == '%')
{
if (fmtStrPtr[1] >= '1' && fmtStrPtr[1] <= '9')
{
S32 argIndex = S32(fmtStrPtr[1] - '0') + 1;
if (argIndex >= argc)
goto done;
const char *argStr = argv[argIndex];
if (!argStr)
goto done;
S32 strLength = dStrlen(argStr);
if (strLength > strMaxLength)
goto done;
dStrcpy(strBufPtr, argStr);
strBufPtr += strLength;
strMaxLength -= strLength;
fmtStrPtr += 2;
continue;
}
}
//if we don't continue, just copy the character
if (strMaxLength <= 0)
goto done;
*strBufPtr++ = *fmtStrPtr++;
strMaxLength--;
}
done:
*strBufPtr = '\0';
return strBuffer;
}
ConsoleFunctionGroupEnd( Net );

View file

@ -0,0 +1,192 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/simBase.h"
#include "platform/event.h"
#include "sim/netConnection.h"
#include "core/stream/bitStream.h"
#include "sim/netObject.h"
#include "console/engineAPI.h"
class SimpleMessageEvent : public NetEvent
{
char *msg;
public:
typedef NetEvent Parent;
SimpleMessageEvent(const char *message = NULL)
{
if(message)
msg = dStrdup(message);
else
msg = NULL;
}
~SimpleMessageEvent()
{ dFree(msg); }
virtual void pack(NetConnection* /*ps*/, BitStream *bstream)
{ bstream->writeString(msg); }
virtual void write(NetConnection*, BitStream *bstream)
{ bstream->writeString(msg); }
virtual void unpack(NetConnection* /*ps*/, BitStream *bstream)
{ char buf[256]; bstream->readString(buf); msg = dStrdup(buf); }
virtual void process(NetConnection *)
{ Con::printf("RMSG %d %s", mSourceId, msg); }
DECLARE_CONOBJECT(SimpleMessageEvent);
};
IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent);
ConsoleDocClass( SimpleMessageEvent,
"@brief A very simple example of a network event.\n\n"
"This object exists purely for instructional purposes. It is primarily "
"geared toward developers that wish to understand the inner-working of "
"Torque 3D's networking system. This is not intended for actual game "
"development.\n\n "
"@see NetEvent for the inner workings of network events\n\n"
"@ingroup Networking\n");
DefineEngineStaticMethod( SimpleMessageEvent, msg, void, (NetConnection* con, const char* message),,
"@brief Send a SimpleMessageEvent message to the specified connection.\n\n"
"The far end that receives the message will print the message out to the console.\n"
"@param con The unique ID of the connection to transmit to\n"
"@param message The string containing the message to transmit\n\n"
"@tsexample\n"
"// Send a message to the other end of the given NetConnection\n"
"SimpleMessageEvent::msg( %conn, \"A message from me!\");\n\n"
"// The far end will see the following in the console\n"
"// (Note: The number may be something other than 1796 as it is the SimObjectID\n"
"// of the received event)\n"
"// \n"
"// RMSG 1796 A message from me!\n"
"@endtsexample\n\n"
)
{
//NetConnection *con = (NetConnection *) Sim::findObject(argv[1]);
if(con)
con->postNetEvent(new SimpleMessageEvent(message));
}
//ConsoleFunction( msg, void, 3, 3, "(NetConnection id, string message)"
// "Send a SimpleNetObject message to the specified connection.")
//{
// NetConnection *con = (NetConnection *) Sim::findObject(argv[1]);
// if(con)
// con->postNetEvent(new SimpleMessageEvent(argv[2]));
//}
class SimpleNetObject : public NetObject
{
typedef NetObject Parent;
public:
char message[256];
SimpleNetObject()
{
mNetFlags.set(ScopeAlways | Ghostable);
dStrcpy(message, "Hello World!");
}
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
{
stream->writeString(message);
return 0;
}
void unpackUpdate(NetConnection *conn, BitStream *stream)
{
stream->readString(message);
Con::printf("Got message: %s", message);
}
void setMessage(const char *msg)
{
setMaskBits(1);
dStrcpy(message, msg);
}
DECLARE_CONOBJECT(SimpleNetObject);
};
IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject);
ConsoleDocClass( SimpleNetObject,
"@brief A very simple example of a class derived from NetObject.\n\n"
"This object exists purely for instructional purposes. It is primarily "
"geared toward developers that wish to understand the inner-working of "
"Torque 3D's networking system. This is not intended for actual game "
"development.\n\n "
"@tsexample\n"
"// On the server, create a new SimpleNetObject. This is a ghost always\n"
"// object so it will be immediately ghosted to all connected clients.\n"
"$s = new SimpleNetObject();\n\n"
"// All connected clients will see the following in their console:\n"
"// \n"
"// Got message: Hello World!\n"
"@endtsexample\n\n"
"@see NetObject for a full breakdown of this example object\n"
"@ingroup Networking\n");
DefineEngineMethod( SimpleNetObject, setMessage, void, (const char* msg),,
"@brief Sets the internal message variable.\n\n"
"SimpleNetObject is set up to automatically transmit this new message to "
"all connected clients. It will appear in the clients' console.\n"
"@param msg The new message to send\n\n"
"@tsexample\n"
"// On the server, create a new SimpleNetObject. This is a ghost always\n"
"// object so it will be immediately ghosted to all connected clients.\n"
"$s = new SimpleNetObject();\n\n"
"// All connected clients will see the following in their console:\n"
"// \n"
"// Got message: Hello World!\n\n"
"// Now again on the server, change the message. This will cause it to\n"
"// be sent to all connected clients.\n"
"$s.setMessage(\"A new message from me!\");\n\n"
"// All connected clients will now see in their console:\n"
"// \n"
"// Go message: A new message from me!\n"
"@endtsexample\n\n"
)
{
object->setMessage(msg);
}
//ConsoleMethod( SimpleNetObject, setMessage, void, 3, 3, "(string msg)")
//{
// object->setMessage(argv[2]);
//}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,128 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SERVERQUERY_H_
#define _SERVERQUERY_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
#ifndef _BITSET_H_
#include "core/bitSet.h"
#endif
#include "platform/platformNet.h"
//-----------------------------------------------------------------------------
// Game Server Information
struct ServerInfo
{
enum StatusFlags
{
// Info flags (0-7):
Status_Dedicated = BIT(0),
Status_Passworded = BIT(1),
Status_Linux = BIT(2),
Status_Xenon = BIT(6),
// Status flags:
Status_New = 0,
Status_Querying = BIT(28),
Status_Updating = BIT(29),
Status_Responded = BIT(30),
Status_TimedOut = BIT(31),
};
U8 numPlayers;
U8 maxPlayers;
U8 numBots;
char* name;
char* gameType;
char* missionName;
char* missionType;
char* statusString;
char* infoString;
NetAddress address;
U32 version;
U32 ping;
U32 cpuSpeed;
bool isFavorite;
BitSet32 status;
ServerInfo()
{
numPlayers = 0;
maxPlayers = 0;
numBots = 0;
name = NULL;
gameType = NULL;
missionType = NULL;
missionName = NULL;
statusString = NULL;
infoString = NULL;
version = 0;
ping = 0;
cpuSpeed = 0;
isFavorite = false;
status = Status_New;
}
~ServerInfo();
bool isNew() { return( status == Status_New ); }
bool isQuerying() { return( status.test( Status_Querying ) ); }
bool isUpdating() { return( status.test( Status_Updating ) ); }
bool hasResponded() { return( status.test( Status_Responded ) ); }
bool isTimedOut() { return( status.test( Status_TimedOut ) ); }
bool isDedicated() { return( status.test( Status_Dedicated ) ); }
bool isPassworded() { return( status.test( Status_Passworded ) ); }
bool isLinux() { return( status.test( Status_Linux ) ); }
bool isXenon() { return( status.test( Status_Xenon ) ); }
};
//-----------------------------------------------------------------------------
extern Vector<ServerInfo> gServerList;
extern bool gServerBrowserDirty;
extern void clearServerList();
extern void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType,
U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
U8 filterFlags);
extern void queryMasterGameTypes();
extern void queryMasterServer(U8 flags, const char* gameType, const char* missionType,
U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
U8 filterFlags, U8 buddyCount, U32* buddyList );
extern void queryFavoriteServers( U8 flags );
extern void querySingleServer(const NetAddress* addr, U8 flags);
extern void startHeartbeat();
extern void sendHeartbeat( U8 flags );
#ifdef TORQUE_DEBUG
extern void addFakeServers( S32 howMany );
#endif // DEBUG
#endif

View file

@ -0,0 +1,521 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "app/net/tcpObject.h"
#include "platform/platform.h"
#include "platform/event.h"
#include "console/simBase.h"
#include "console/consoleInternal.h"
#include "core/strings/stringUnit.h"
#include "console/engineAPI.h"
TCPObject *TCPObject::table[TCPObject::TableSize] = {0, };
IMPLEMENT_CONOBJECT(TCPObject);
ConsoleDocClass( TCPObject,
"@brief Allows communications between the game and a server using TCP/IP protocols.\n\n"
"To use TCPObject you set up a connection to a server, send data to the server, and handle "
"each line of the server's response using a callback. Once you are done communicating with "
"the server, you disconnect.\n\n"
"TCPObject is intended to be used with text based protocols which means you'll need to "
"delineate the server's response with an end-of-line character. i.e. the newline "
"character @\\n. You may optionally include the carriage return character @\\r prior to the newline "
"and TCPObject will strip it out before sending the line to the callback. If a newline "
"character is not included in the server's output, the received data will not be "
"processed until you disconnect from the server (which flushes the internal buffer).\n\n"
"TCPObject may also be set up to listen to a specific port, making Torque into a TCP server. "
"When used in this manner, a callback is received when a client connection is made. Following "
"the outside connection, text may be sent and lines are processed in the usual manner.\n\n"
"If you want to work with HTTP you may wish to use HTTPObject instead as it handles all of the "
"HTTP header setup and parsing.\n\n"
"@tsexample\n"
"// In this example we'll retrieve the new forum threads RSS\n"
"// feed from garagegames.com. As we're using TCPObject, the\n"
"// raw text response will be received from the server, including\n"
"// the HTTP header.\n\n"
"// Define callbacks for our specific TCPObject using our instance's\n"
"// name (RSSFeed) as the namespace.\n\n"
"// Handle an issue with resolving the server's name\n"
"function RSSFeed::onDNSFailed(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"DNSFailed\";\n\n"
" // Handle DNS failure\n"
"}\n\n"
"function RSSFeed::onConnectFailed(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"ConnectFailed\";\n\n"
" // Handle connection failure\n"
"}\n\n"
"function RSSFeed::onDNSResolved(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"DNSResolved\";\n\n"
"}\n\n"
"function RSSFeed::onConnected(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"Connected\";\n\n"
"}\n\n"
"function RSSFeed::onDisconnect(%this)\n"
"{\n"
" // Store this state\n"
" %this.lastState = \"Disconnected\";\n\n"
"}\n\n"
"// Handle a line from the server\n"
"function RSSFeed::onLine(%this, %line)\n"
"{\n"
" // Print the line to the console\n"
" echo( %line );\n"
"}\n\n"
"// Create the TCPObject\n"
"%rss = new TCPObject(RSSFeed);\n\n"
"// Define a dynamic field to store the last connection state\n"
"%rss.lastState = \"None\";\n\n"
"// Connect to the server\n"
"%rss.connect(\"www.garagegames.com:80\");\n\n"
"// Send the RSS feed request to the server. Response will be\n"
"// handled in onLine() callback above\n"
"%rss.send(\"GET /feeds/rss/threads HTTP/1.1\\r\\nHost: www.garagegames.com\\r\\n\\r\\n\");\n"
"@endtsexample\n\n"
"@see HTTPObject\n"
"@ingroup Networking\n"
);
IMPLEMENT_CALLBACK(TCPObject, onConnectionRequest, void, (const char* address, const char* ID), (address, ID),
"@brief Called whenever a connection request is made.\n\n"
"This callback is used when the TCPObject is listening to a port and a client is attempting to connect.\n"
"@param address Server address connecting from.\n"
"@param ID Connection ID\n"
);
IMPLEMENT_CALLBACK(TCPObject, onLine, void, (const char* line), (line),
"@brief Called whenever a line of data is sent to this TCPObject.\n\n"
"This callback is called when the received data contains a newline @\\n character, or "
"the connection has been disconnected and the TCPObject's buffer is flushed.\n"
"@param line Data sent from the server.\n"
);
IMPLEMENT_CALLBACK(TCPObject, onDNSResolved, void, (),(),
"Called whenever the DNS has been resolved.\n"
);
IMPLEMENT_CALLBACK(TCPObject, onDNSFailed, void, (),(),
"Called whenever the DNS has failed to resolve.\n"
);
IMPLEMENT_CALLBACK(TCPObject, onConnected, void, (),(),
"Called whenever a connection is established with a server.\n"
);
IMPLEMENT_CALLBACK(TCPObject, onConnectFailed, void, (),(),
"Called whenever a connection has failed to be established with a server.\n"
);
IMPLEMENT_CALLBACK(TCPObject, onDisconnect, void, (),(),
"Called whenever the TCPObject disconnects from whatever it is currently connected to.\n"
);
TCPObject *TCPObject::find(NetSocket tag)
{
for(TCPObject *walk = table[U32(tag) & TableMask]; walk; walk = walk->mNext)
if(walk->mTag == tag)
return walk;
return NULL;
}
void TCPObject::addToTable(NetSocket newTag)
{
removeFromTable();
mTag = newTag;
mNext = table[U32(mTag) & TableMask];
table[U32(mTag) & TableMask] = this;
}
void TCPObject::removeFromTable()
{
for(TCPObject **walk = &table[U32(mTag) & TableMask]; *walk; walk = &((*walk)->mNext))
{
if(*walk == this)
{
*walk = mNext;
return;
}
}
}
void processConnectedReceiveEvent(NetSocket sock, RawData incomingData);
void processConnectedAcceptEvent(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress);
void processConnectedNotifyEvent( NetSocket sock, U32 state );
S32 gTCPCount = 0;
TCPObject::TCPObject()
{
mBuffer = NULL;
mBufferSize = 0;
mPort = 0;
mTag = InvalidSocket;
mNext = NULL;
mState = Disconnected;
gTCPCount++;
if(gTCPCount == 1)
{
Net::smConnectionAccept.notify(processConnectedAcceptEvent);
Net::smConnectionReceive.notify(processConnectedReceiveEvent);
Net::smConnectionNotify.notify(processConnectedNotifyEvent);
}
}
TCPObject::~TCPObject()
{
disconnect();
dFree(mBuffer);
gTCPCount--;
if(gTCPCount == 0)
{
Net::smConnectionAccept.remove(processConnectedAcceptEvent);
Net::smConnectionReceive.remove(processConnectedReceiveEvent);
Net::smConnectionNotify.remove(processConnectedNotifyEvent);
}
}
bool TCPObject::processArguments(S32 argc, const char **argv)
{
if(argc == 0)
return true;
else if(argc == 1)
{
addToTable(U32(dAtoi(argv[0])));
return true;
}
return false;
}
bool TCPObject::onAdd()
{
if(!Parent::onAdd())
return false;
const char *name = getName();
if(name && name[0] && getClassRep())
{
Namespace *parent = getClassRep()->getNameSpace();
Con::linkNamespaces(parent->mName, name);
mNameSpace = Con::lookupNamespace(name);
}
Sim::getTCPGroup()->addObject(this);
return true;
}
U32 TCPObject::onReceive(U8 *buffer, U32 bufferLen)
{
// we got a raw buffer event
// default action is to split the buffer into lines of text
// and call processLine on each
// for any incomplete lines we have mBuffer
U32 start = 0;
parseLine(buffer, &start, bufferLen);
return start;
}
void TCPObject::parseLine(U8 *buffer, U32 *start, U32 bufferLen)
{
// find the first \n in buffer
U32 i;
U8 *line = buffer + *start;
for(i = *start; i < bufferLen; i++)
if(buffer[i] == '\n' || buffer[i] == 0)
break;
U32 len = i - *start;
if(i == bufferLen || mBuffer)
{
// we've hit the end with no newline
mBuffer = (U8 *) dRealloc(mBuffer, mBufferSize + len + 1);
dMemcpy(mBuffer + mBufferSize, line, len);
mBufferSize += len;
*start = i;
// process the line
if(i != bufferLen)
{
mBuffer[mBufferSize] = 0;
if(mBufferSize && mBuffer[mBufferSize-1] == '\r')
mBuffer[mBufferSize - 1] = 0;
U8 *temp = mBuffer;
mBuffer = 0;
mBufferSize = 0;
processLine((UTF8*)temp);
dFree(temp);
}
}
else if(i != bufferLen)
{
line[len] = 0;
if(len && line[len-1] == '\r')
line[len-1] = 0;
processLine((UTF8*)line);
}
if(i != bufferLen)
*start = i + 1;
}
void TCPObject::onConnectionRequest(const NetAddress *addr, U32 connectId)
{
char idBuf[16];
char addrBuf[256];
Net::addressToString(addr, addrBuf);
dSprintf(idBuf, sizeof(idBuf), "%d", connectId);
onConnectionRequest_callback(addrBuf,idBuf);
}
bool TCPObject::processLine(UTF8 *line)
{
onLine_callback(line);
return true;
}
void TCPObject::onDNSResolved()
{
mState = DNSResolved;
onDNSResolved_callback();
}
void TCPObject::onDNSFailed()
{
mState = Disconnected;
onDNSFailed_callback();
}
void TCPObject::onConnected()
{
mState = Connected;
onConnected_callback();
}
void TCPObject::onConnectFailed()
{
mState = Disconnected;
onConnectFailed_callback();
}
void TCPObject::finishLastLine()
{
if(mBufferSize)
{
mBuffer[mBufferSize] = 0;
processLine((UTF8*)mBuffer);
dFree(mBuffer);
mBuffer = 0;
mBufferSize = 0;
}
}
void TCPObject::onDisconnect()
{
finishLastLine();
mState = Disconnected;
onDisconnect_callback();
}
void TCPObject::listen(U16 port)
{
mState = Listening;
U32 newTag = Net::openListenPort(port);
addToTable(newTag);
}
void TCPObject::connect(const char *address)
{
NetSocket newTag = Net::openConnectTo(address);
addToTable(newTag);
}
void TCPObject::disconnect()
{
if( mTag != InvalidSocket ) {
Net::closeConnectTo(mTag);
}
removeFromTable();
}
void TCPObject::send(const U8 *buffer, U32 len)
{
Net::sendtoSocket(mTag, buffer, S32(len));
}
DefineEngineMethod(TCPObject, send, void, (const char *data),,
"@brief Transmits the data string to the connected computer.\n\n"
"This method is used to send text data to the connected computer regardless if we initiated the "
"connection using connect(), or listening to a port using listen().\n"
"@param data The data string to send.\n"
"@tsexample\n"
"// Set the command data\n"
"%data = \"GET \" @ $RSSFeed::serverURL @ \" HTTP/1.0\\r\\n\";\n"
"%data = %data @ \"Host: \" @ $RSSFeed::serverName @ \"\\r\\n\";\n"
"%data = %data @ \"User-Agent: \" @ $RSSFeed::userAgent @ \"\\r\\n\\r\\n\"\n\n"
"// Send the command to the connected server.\n"
"%thisTCPObj.send(%data);\n"
"@endtsexample\n")
{
object->send( (const U8*)data, dStrlen(data) );
}
DefineEngineMethod(TCPObject, listen, void, (int port),,
"@brief Start listening on the specified port for connections.\n\n"
"@param port Port for this TCPObject to start listening for connections on.\n"
"@tsexample\n"
"// Set the port number list\n"
"%portNumber = 80;\n\n"
"// Inform this TCPObject to start listening at the specified port.\n"
"%thisTCPObj.send(%portNumber);\n"
"@endtsexample\n")
{
object->listen(U32(port));
}
DefineEngineMethod(TCPObject, connect, void, (const char* address),,
"@brief Connect to the given address.\n\n"
"@param address Server address (including port) to connect to.\n"
"@tsexample\n"
"// Set the address.\n"
"%address = \"www.garagegames.com:80\";\n\n"
"// Inform this TCPObject to connect to the specified address.\n"
"%thisTCPObj.connect(%address);\n"
"@endtsexample\n")
{
object->connect(address);
}
DefineEngineMethod(TCPObject, disconnect, void, (),,
"@brief Disconnect from whatever this TCPObject is currently connected to, if anything.\n\n"
"@tsexample\n"
"// Inform this TCPObject to disconnect from anything it is currently connected to.\n"
"%thisTCPObj.disconnect();\n"
"@endtsexample\n")
{
object->disconnect();
}
void processConnectedReceiveEvent(NetSocket sock, RawData incomingData)
{
TCPObject *tcpo = TCPObject::find(sock);
if(!tcpo)
{
Con::printf("Got bad connected receive event.");
return;
}
U32 size = incomingData.size;
U8 *buffer = (U8*)incomingData.data;
while(size)
{
U32 ret = tcpo->onReceive(buffer, size);
AssertFatal(ret <= size, "Invalid return size");
size -= ret;
buffer += ret;
}
}
void processConnectedAcceptEvent(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress)
{
TCPObject *tcpo = TCPObject::find(listeningPort);
if(!tcpo)
return;
tcpo->onConnectionRequest(&originatingAddress, newConnection);
}
void processConnectedNotifyEvent( NetSocket sock, U32 state )
{
TCPObject *tcpo = TCPObject::find(sock);
if(!tcpo)
return;
switch(state)
{
case Net::DNSResolved:
tcpo->onDNSResolved();
break;
case Net::DNSFailed:
tcpo->onDNSFailed();
break;
case Net::Connected:
tcpo->onConnected();
break;
case Net::ConnectFailed:
tcpo->onConnectFailed();
break;
case Net::Disconnected:
tcpo->onDisconnect();
break;
}
}

View file

@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TCPOBJECT_H_
#define _TCPOBJECT_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#include "platform/platformNet.h"
class TCPObject : public SimObject
{
public:
enum State {Disconnected, DNSResolved, Connected, Listening };
DECLARE_CALLBACK(void, onConnectionRequest, (const char* address, const char* ID));
DECLARE_CALLBACK(void, onLine, (const char* line));
DECLARE_CALLBACK(void, onDNSResolved,());
DECLARE_CALLBACK(void, onDNSFailed, ());
DECLARE_CALLBACK(void, onConnected, ());
DECLARE_CALLBACK(void, onConnectFailed, ());
DECLARE_CALLBACK(void, onDisconnect, ());
private:
NetSocket mTag;
TCPObject *mNext;
enum { TableSize = 256, TableMask = 0xFF };
static TCPObject *table[TableSize];
State mState;
protected:
typedef SimObject Parent;
U8 *mBuffer;
U32 mBufferSize;
U16 mPort;
public:
TCPObject();
virtual ~TCPObject();
void parseLine(U8 *buffer, U32 *start, U32 bufferLen);
void finishLastLine();
static TCPObject *find(NetSocket tag);
// onReceive gets called continuously until all bytes are processed
// return # of bytes processed each time.
virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data
virtual bool processLine(UTF8 *line); // process a complete line of text... default action is to call into script
virtual void onDNSResolved();
virtual void onDNSFailed();
virtual void onConnected();
virtual void onConnectFailed();
virtual void onConnectionRequest(const NetAddress *addr, U32 connectId);
virtual void onDisconnect();
void connect(const char *address);
void listen(U16 port);
void disconnect();
State getState() { return mState; }
bool processArguments(S32 argc, const char **argv);
void send(const U8 *buffer, U32 bufferLen);
void addToTable(NetSocket newTag);
void removeFromTable();
void setPort(U16 port) { mPort = port; }
bool onAdd();
DECLARE_CONOBJECT(TCPObject);
};
#endif // _H_TCPOBJECT_

View file

@ -0,0 +1,132 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "app/version.h"
#include "console/console.h"
static const U32 csgVersionNumber = TORQUE_GAME_ENGINE;
U32 getVersionNumber()
{
return csgVersionNumber;
}
const char* getVersionString()
{
return TORQUE_GAME_ENGINE_VERSION_STRING;
}
/// TGE 0001
/// TGEA 0002
/// TGB 0003
/// TGEA 360 0004
/// TGE WII 0005
/// Torque 3D 0006
const char* getEngineProductString()
{
#ifndef TORQUE_ENGINE_PRODUCT
return "Torque Engine";
#else
switch (TORQUE_ENGINE_PRODUCT)
{
case 0001:
return "Torque Game Engine";
case 0002:
return "Torque Game Engine Advanced";
case 0003:
return "Torque 2D";
case 0004:
return "Torque 360";
case 0005:
return "Torque for Wii";
case 0006:
return "Torque 3D";
default:
return "Torque Engine";
};
#endif
}
const char* getCompileTimeString()
{
return __DATE__ " at " __TIME__;
}
//----------------------------------------------------------------
ConsoleFunctionGroupBegin( CompileInformation, "Functions to get version information about the current executable." );
ConsoleFunction( getVersionNumber, S32, 1, 1, "Get the version of the build, as a string.\n\n"
"@ingroup Debugging")
{
return getVersionNumber();
}
ConsoleFunction( getVersionString, const char*, 1, 1, "Get the version of the build, as a string.\n\n"
"@ingroup Debugging")
{
return getVersionString();
}
ConsoleFunction( getEngineName, const char*, 1, 1, "Get the name of the engine product that this is running from, as a string.\n\n"
"@ingroup Debugging")
{
return getEngineProductString();
}
ConsoleFunction( getCompileTimeString, const char*, 1, 1, "Get the time of compilation.\n\n"
"@ingroup Debugging")
{
return getCompileTimeString();
}
ConsoleFunction( getBuildString, const char*, 1, 1, "Get the type of build, \"Debug\" or \"Release\".\n\n"
"@ingroup Debugging")
{
#ifdef TORQUE_DEBUG
return "Debug";
#else
return "Release";
#endif
}
ConsoleFunctionGroupEnd( CompileInformation );
ConsoleFunction(isDemo, bool, 1, 1, "")
{
#ifdef TORQUE_DEMO
return true;
#else
return false;
#endif
}
ConsoleFunction(isWebDemo, bool, 1, 1, "")
{
#ifdef TORQUE_DEMO
return Platform::getWebDeployment();
#else
return false;
#endif
}

View file

@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _VERSION_H_
#define _VERSION_H_
/// This is our global version number for the engine source code that
/// we are using. See <game>/source/torqueConfig.h for the game's source
/// code version, the game name, and which type of game it is (TGB, TGE, TGEA, etc.).
///
/// Version number is major * 1000 + minor * 100 + revision * 10.
#define TORQUE_GAME_ENGINE 1100
/// Human readable engine version string.
#define TORQUE_GAME_ENGINE_VERSION_STRING "2011"
/// Gets the specified version number. The version number is specified as a global in version.cc
U32 getVersionNumber();
/// Gets the version number in string form
const char* getVersionString();
/// Gets the engine product name in string form
const char* getEngineProductString();
/// Gets the compile date and time
const char* getCompileTimeString();
#endif