Thread: [Opentnl-cvs] tnl/test Makefile,NONE,1.1 TNLTest.def,NONE,1.1 TNLTest.dsp,NONE,1.1 TNLTest.suo,NONE,
Brought to you by:
mark_frohnmayer,
s_alanet
From: Ben G. <s_a...@us...> - 2004-04-20 04:27:30
|
Update of /cvsroot/opentnl/tnl/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31063/test Added Files: Makefile TNLTest.def TNLTest.dsp TNLTest.suo TNLTest.vcproj client.cpp fractal.suo mondrian.ico server.cpp server.dsp server.vcproj test.rc test.suo testGame.cpp testGame.h testGameRender.cpp testWindow.cpp Log Message: Initial commit. --- NEW FILE: test.suo --- (This appears to be a binary file; contents omitted.) --- NEW FILE: server.vcproj --- <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="7.10" Name="server" ProjectGUID="{2BC8A06B-B9E6-4C92-9E10-6B5E37CBFF96}" Keyword="Win32Proj"> <Platforms> <Platform Name="Win32"/> </Platforms> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="Debug" IntermediateDirectory="Debug" ConfigurationType="1" CharacterSet="2"> <Tool Name="VCCLCompilerTool" Optimization="0" PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG_NET;TNL_ENABLE_ASSERTS" MinimalRebuild="TRUE" BasicRuntimeChecks="3" RuntimeLibrary="5" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="TRUE" DebugInformationFormat="4"/> <Tool Name="VCCustomBuildTool"/> <Tool Name="VCLinkerTool" AdditionalDependencies="Iphlpapi.lib wsock32.lib" OutputFile="../exe/serverd.exe" LinkIncremental="2" IgnoreDefaultLibraryNames="MSVCRTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(OutDir)/server.pdb" SubSystem="1" TargetMachine="1"/> <Tool Name="VCMIDLTool"/> <Tool Name="VCPostBuildEventTool"/> <Tool Name="VCPreBuildEventTool"/> <Tool Name="VCPreLinkEventTool"/> <Tool Name="VCResourceCompilerTool"/> <Tool Name="VCWebServiceProxyGeneratorTool"/> <Tool Name="VCXMLDataGeneratorTool"/> <Tool Name="VCWebDeploymentTool"/> <Tool Name="VCManagedWrapperGeneratorTool"/> <Tool Name="VCAuxiliaryManagedWrapperGeneratorTool"/> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="Release" IntermediateDirectory="Release" ConfigurationType="1" CharacterSet="2"> <Tool Name="VCCLCompilerTool" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="4" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="TRUE" DebugInformationFormat="3"/> <Tool Name="VCCustomBuildTool"/> <Tool Name="VCLinkerTool" AdditionalDependencies="Iphlpapi.lib wsock32.lib" OutputFile="../exe/server.exe" LinkIncremental="1" IgnoreDefaultLibraryNames="MSVCRT" GenerateDebugInformation="TRUE" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" TargetMachine="1"/> <Tool Name="VCMIDLTool"/> <Tool Name="VCPostBuildEventTool"/> <Tool Name="VCPreBuildEventTool"/> <Tool Name="VCPreLinkEventTool"/> <Tool Name="VCResourceCompilerTool"/> <Tool Name="VCWebServiceProxyGeneratorTool"/> <Tool Name="VCXMLDataGeneratorTool"/> <Tool Name="VCWebDeploymentTool"/> <Tool Name="VCManagedWrapperGeneratorTool"/> <Tool Name="VCAuxiliaryManagedWrapperGeneratorTool"/> </Configuration> </Configurations> <References> </References> <Files> <File RelativePath=".\server.cpp"> </File> <File RelativePath=".\testGame.cpp"> </File> </Files> <Globals> </Globals> </VisualStudioProject> --- NEW FILE: TNLTest.vcproj --- <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="7.10" Name="TNLTest" ProjectGUID="{2BF337F1-B812-4324-A9BA-E4D580C8CFF1}" SccProjectName="" SccLocalPath=""> <Platforms> <Platform Name="Win32"/> </Platforms> <Configurations> <Configuration Name="Release|Win32" OutputDirectory="..\exe" IntermediateDirectory=".\Release" ConfigurationType="1" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="FALSE"> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" AdditionalIncludeDirectories="" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;WINVER=0x400;_MT;wxUSE_GUI=1" StringPooling="TRUE" ExceptionHandling="FALSE" RuntimeLibrary="2" EnableFunctionLevelLinking="TRUE" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="2" PrecompiledHeaderFile=".\Release/TNLTest.pch" AssemblerListingLocation=".\Release/" ObjectFile=".\Release/" ProgramDataBaseFileName=".\Release/" WarningLevel="3" SuppressStartupBanner="TRUE" DebugInformationFormat="3" CompileAs="0"/> <Tool Name="VCCustomBuildTool"/> <Tool Name="VCLinkerTool" AdditionalDependencies="Iphlpapi.lib libtomcrypt.lib opengl32.lib glu32.lib odbc32.lib odbccp32.lib comctl32.lib wsock32.lib tnl.lib ../glut/glut32.lib" OutputFile="..\exe\TNLTest.exe" LinkIncremental="1" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="../wxWindows/lib;../lib" IgnoreDefaultLibraryNames="LIBC" GenerateDebugInformation="TRUE" ProgramDatabaseFile=".\Release/TNLTest.pdb" SubSystem="1" TargetMachine="1"/> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="TRUE" SuppressStartupBanner="TRUE" TargetEnvironment="1" TypeLibraryName=".\Release/TNLTest.tlb" HeaderFileName=""/> <Tool Name="VCPostBuildEventTool"/> <Tool Name="VCPreBuildEventTool"/> <Tool Name="VCPreLinkEventTool"/> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1033" AdditionalIncludeDirectories="..\wxWindows\include"/> <Tool Name="VCWebServiceProxyGeneratorTool"/> <Tool Name="VCXMLDataGeneratorTool"/> <Tool Name="VCWebDeploymentTool"/> <Tool Name="VCManagedWrapperGeneratorTool"/> <Tool Name="VCAuxiliaryManagedWrapperGeneratorTool"/> </Configuration> <Configuration Name="Debug|Win32" OutputDirectory="..\exe" IntermediateDirectory=".\Debug" ConfigurationType="1" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="FALSE"> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\wxWindows\include,..\wxWindows\lib/mswd,..\wxWindows\lib\mswd" PreprocessorDefinitions="TNL_ENABLE_LOGGING;TNL_ENABLE_ASSERTS;NDEBUG;WIN32;_WINDOWS;WINVER=0x400;_MT;wxUSE_GUI=1;" RuntimeLibrary="3" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="2" PrecompiledHeaderFile=".\Debug/TNLTest.pch" AssemblerListingLocation=".\Debug/" ObjectFile=".\Debug/" ProgramDataBaseFileName=".\Debug/" WarningLevel="3" SuppressStartupBanner="TRUE" DebugInformationFormat="3" CompileAs="0"/> <Tool Name="VCCustomBuildTool"/> <Tool Name="VCLinkerTool" AdditionalDependencies="Iphlpapi.lib libtomcryptd.lib tnld.lib opengl32.lib glu32.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib ../glut/glut32.lib" OutputFile="..\exe\TNLTestD.exe" LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="../lib" IgnoreDefaultLibraryNames="MSVCRT,LIBCD" GenerateDebugInformation="TRUE" ProgramDatabaseFile=".\Debug/TNLTest.pdb" SubSystem="1" TargetMachine="1"/> <Tool Name="VCMIDLTool" PreprocessorDefinitions="_DEBUG" MkTypLibCompatible="TRUE" SuppressStartupBanner="TRUE" TargetEnvironment="1" TypeLibraryName=".\Debug/TNLTest.tlb" HeaderFileName=""/> <Tool Name="VCPostBuildEventTool"/> <Tool Name="VCPreBuildEventTool"/> <Tool Name="VCPreLinkEventTool"/> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="_DEBUG" Culture="1033" AdditionalIncludeDirectories="..\wxWindows\include"/> <Tool Name="VCWebServiceProxyGeneratorTool"/> <Tool Name="VCXMLDataGeneratorTool"/> <Tool Name="VCWebDeploymentTool"/> <Tool Name="VCManagedWrapperGeneratorTool"/> <Tool Name="VCAuxiliaryManagedWrapperGeneratorTool"/> </Configuration> </Configurations> <References> </References> <Files> <File RelativePath=".\testGame.cpp"> </File> <File RelativePath=".\testGame.h"> </File> <File RelativePath=".\testGameRender.cpp"> </File> <File RelativePath=".\testWindow.cpp"> </File> </Files> <Globals> </Globals> </VisualStudioProject> --- NEW FILE: testGame.cpp --- //----------------------------------------------------------------------------------- // // Torque Network Library - TNLTest example program // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #include <math.h> #include "testGame.h" #include "../tnl/tnlBitStream.h" #include "../tnl/tnlNetConnection.h" #include "../tnl/tnlRandom.h" #include "../tnl/tnlSymmetricCipher.h" #include "../tnl/tnlAsymmetricKey.h" namespace TNLTest { TestGame *clientGame = NULL; TestGame *serverGame = NULL; //--------------------------------------------------------------------------------- TNL_IMPLEMENT_NETOBJECT(Player); // Player constructor Player::Player(Player::PlayerType pt) { // assign a random starting position for the player. startPos.x = Random::readF(); startPos.y = Random::readF(); endPos.x = startPos.x; endPos.y = startPos.y; renderPos = startPos; // preset the movement parameter for the player to the end of // the path t = 1.0; tDelta = 0; myPlayerType = pt; mNetFlags.set(Ghostable); game = NULL; } Player::~Player() { if(!game) return; // remove the player from the list of players in the game. for( S32 i = 0; i < game->players.size(); i++) { if(game->players[i] == this) { game->players.erase_fast(i); return; } } } void Player::addToGame(TestGame *theGame) { // add the player to the list of players in the game. theGame->players.push_back(this); game = theGame; if(myPlayerType == PlayerTypeMyClient) { // set the client player for the game for drawing the // scoping radius circle. game->clientPlayer = this; } } bool Player::onGhostAdd(GhostConnection *theConnection) { addToGame(((TestNetInterface *) theConnection->getInterface())->game); return true; } void Player::performScopeQuery(GhostConnection *connection) { // find all the objects that are "in scope" - for the purposes // of this test program, that is all objects in a circle of radius // 0.25 around the scope object, or a radius squared of 0.0625 for(S32 i = 0; i < game->players.size(); i++) { Position playerP = game->players[i]->renderPos; F32 dx = playerP.x - renderPos.x; F32 dy = playerP.y - renderPos.y; F32 distSquared = dx * dx + dy * dy; if(distSquared < 0.0625) connection->objectInScope(game->players[i]); } } U32 Player::packUpdate(GhostConnection *connection, U32 updateMask, BitStream *stream) { // if this is the initial update, write out some static // information about this player. Since we never call // setMaskBits(InitialMask), we're guaranteed that this state // will only be updated on the first update for this object. if(stream->writeFlag(updateMask & InitialMask)) { // write one bit if it's not an AI if(stream->writeFlag(myPlayerType != PlayerTypeAI)) { // write a flag to the client if this is the player that // that client controls. stream->writeFlag(connection->getScopeObject() == this); } } // see if we need to write out the position data // we must write a bit to let the unpackUpdate know whether to // read the position data out of the stream. if(stream->writeFlag(updateMask & PositionMask)) { // since the position data is all 0 to 1, we can use // the bit stream's writeFloat method to write it out. // 12 bits should be sufficient precision stream->writeFloat(startPos.x, 12); stream->writeFloat(startPos.y, 12); stream->writeFloat(endPos.x, 12); stream->writeFloat(endPos.y, 12); stream->write(t); // fully precise t and tDelta stream->write(tDelta); } // if I had other states on the player that changed independently // of position, I could test for and update them here. // ... // the return value from packUpdate is the update mask for this // object on this client _AFTER_ this update has been sent. // Normally this will be zero, but it could be nonzero if this // object has a state that depends on some other object that // hasn't yet been ghosted. return 0; } void Player::unpackUpdate(GhostConnection *connection, BitStream *stream) { // see if the initial packet data was written: if(stream->readFlag()) { // check to see if it's not an AI player. if(stream->readFlag()) { if(stream->readFlag()) myPlayerType = PlayerTypeMyClient; else myPlayerType = PlayerTypeClient; } else myPlayerType = PlayerTypeAIDummy; } // see if the player's position has been updated: if(stream->readFlag()) { startPos.x = stream->readFloat(12); startPos.y = stream->readFloat(12); endPos.x = stream->readFloat(12); endPos.y = stream->readFloat(12); stream->read(&t); stream->read(&tDelta); // update the render position update(0); } } void Player::serverSetPosition(Position inStartPos, Position inEndPos, F32 inT, F32 inTDelta) { // update the instance variables of the object startPos = inStartPos; endPos = inEndPos; t = inT; tDelta = inTDelta; // notify the network system that the position state of this object has changed: setMaskBits(PositionMask); // call a quick RPC to all the connections that have this object in scope rpcPlayerDidMove(inEndPos.x, inEndPos.y); } void Player::update(F32 timeDelta) { t += tDelta * timeDelta; if(t >= 1.0) { t = 1.0; tDelta = 0; renderPos = endPos; // if this is an AI player on the server, if(myPlayerType == PlayerTypeAI) { startPos = renderPos; t = 0; endPos.x = Random::readF(); endPos.y = Random::readF(); tDelta = 0.2f + Random::readF() * 0.1f; setMaskBits(PositionMask); // notify the network system that the network state has been updated } } renderPos.x = startPos.x + (endPos.x - startPos.x) * t; renderPos.y = startPos.y + (endPos.y - startPos.y) * t; } void Player::onGhostAvailable(GhostConnection *theConnection) { // this function is called every time a ghost of this object is known // to be available on a given client. // we'll use this to demonstrate targeting a NetObject RPC // to a specific connection. Normally a RPC method marked as // RPCToGhost will be broadcast to ALL ghosts of the object currently in // scope. // first we construct an event that will represent the RPC call... // the RPC macros create a RPCNAME_construct function that returns // a NetEvent that encapsulates the RPC. NetEvent *theRPCEvent = TNL_RPC_CONSTRUCT_NETEVENT(this, rpcPlayerIsInScope, (renderPos.x, renderPos.y)); // then we can just post the event to whatever connections we want to have // receive the message. // In the case of NetObject RPCs, if the source object is not ghosted, // the RPC will just be silently dropped. theConnection->postNetEvent(theRPCEvent); } TNL_IMPLEMENT_NETOBJECT_RPC(Player, rpcPlayerIsInScope, (Float<6> x, Float<6> y), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCToGhost, 0) { F32 fx = x, fy = y; logprintf("A player is now in scope at %g, %g", fx, fy); } TNL_IMPLEMENT_NETOBJECT_RPC(Player, rpcPlayerWillMove, (const char *testString), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCToGhostParent, 0) { logprintf("Expecting a player move from the connection: %s", testString); } TNL_IMPLEMENT_NETOBJECT_RPC(Player, rpcPlayerDidMove, (Float<6> x, Float<6> y), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCToGhost, 0) { F32 fx = x, fy = y; logprintf("A player moved to %g, %g", fx, fy); } //--------------------------------------------------------------------------------- TNL_IMPLEMENT_NETOBJECT(Building); Building::Building() { // place the "building" in a random position on the screen upperLeft.x = Random::readF(); upperLeft.y = Random::readF(); lowerRight.x = upperLeft.x + Random::readF() * 0.1f + 0.025f; lowerRight.y = upperLeft.y + Random::readF() * 0.1f + 0.025f; game = NULL; // always scope the buildings to the clients mNetFlags.set(Ghostable); setScopeAlways(); } Building::~Building() { if(!game) return; // remove the building from the list of buildings in the game. for( S32 i = 0; i < game->buildings.size(); i++) { if(game->buildings[i] == this) { game->buildings.erase_fast(i); return; } } } void Building::addToGame(TestGame *theGame) { // add it to the list of buildings in the game theGame->buildings.push_back(this); game = theGame; } bool Building::onGhostAdd(GhostConnection *theConnection) { addToGame(((TestNetInterface *) theConnection->getInterface())->game); return true; } U32 Building::packUpdate(GhostConnection *connection, U32 updateMask, BitStream *stream) { if(stream->writeFlag(updateMask & InitialMask)) { // we know all the positions are 0 to 1. // since this object is scope always, we don't care quite as much // how efficient the initial state updating is, since we're only // ever going to send this data when the client first connects // to this mission. stream->write(upperLeft.x); stream->write(upperLeft.y); stream->write(lowerRight.x); stream->write(lowerRight.y); } // for later - add a "color" field to the buildings that can change // when AIs or players move over them. return 0; } void Building::unpackUpdate(GhostConnection *connection, BitStream *stream) { if(stream->readFlag()) { stream->read(&upperLeft.x); stream->read(&upperLeft.y); stream->read(&lowerRight.x); stream->read(&lowerRight.y); } } //--------------------------------------------------------------------------------- TNL_IMPLEMENT_NETCONNECTION(TestConnection, NetClassGroupGame, true); TNL_DECLARE_RPC_MEM_ENUM(TestConnection, PlayerPosReplyBitSize); TNL_IMPLEMENT_RPC(TestConnection, rpcGotPlayerPos, (bool b1, bool b2, StringTableEntry string, Float<TestConnection::PlayerPosReplyBitSize> x, Float<TestConnection::PlayerPosReplyBitSize> y), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCDirAny, 0) { F32 xv = x, yv = y; logprintf("Server acknowledged position update - %d %d %s %g %g", b1, b2, string.getString(), xv, yv); } TNL_IMPLEMENT_RPC(TestConnection, rpcSetPlayerPos, (F32 x, F32 y), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCDirClientToServer, 0) { Position newPosition; newPosition.x = x; newPosition.y = y; logprintf("%s - received new position (%g, %g) from client", getNetAddressString(), newPosition.x, newPosition.y); myPlayer->serverSetPosition(myPlayer->renderPos, newPosition, 0, 0.2f); // send an RPC back the other way! StringTableEntry helloString("Hello World!!"); rpcGotPlayerPos(true, false, helloString, x, y); }; TestConnection::TestConnection() { // every connection class instance must have a net class group set // before it is used. //setIsAdaptive(); // <-- Uncomment me if you want to use adaptive rate instead of fixed rate... } void TestConnection::onTimedOut() { logprintf("%s - connection to %s timed out.", getNetAddressString(), isConnectionToServer() ? "server" : "client" ); // if this is a client to server connection, set us back to pinging servers. if(isConnectionToServer()) ((TestNetInterface *) getInterface())->pingingServers = true; else delete (Player*)myPlayer; // delete the client's player object. } void TestConnection::onConnectTimedOut() { ((TestNetInterface *) getInterface())->pingingServers = true; } void TestConnection::onDisconnect(const char *reason) { logprintf("%s - %s disconnected.", getNetAddressString(), isConnectionToServer() ? "server" : "client" ); if(isConnectionToServer()) ((TestNetInterface *) getInterface())->pingingServers = true; else delete (Player*)myPlayer; } void TestConnection::onConnectionEstablished(bool isInitiator) { // call the parent's onConnectionEstablished. // by default this will set the initiator to be a connection // to "server" and the non-initiator to be a connnection to "client" Parent::onConnectionEstablished(isInitiator); // To see how this program performs with 50% packet loss, // Try uncommenting the next line :) //setSimulatedNetParams(0.5, 0); if(isInitiator) { setGhostFrom(false); setGhostTo(true); logprintf("%s - connected to server.", getNetAddressString()); ((TestNetInterface *) getInterface())->connectionToServer = this; } else { // on the server, we create a player object that will be the scope object // for this client. Player *player = new Player; myPlayer = player; myPlayer->setInterface(getInterface()); myPlayer->addToGame(((TestNetInterface *) getInterface())->game); setScopeObject(myPlayer); setGhostFrom(true); setGhostTo(false); activateGhosting(); logprintf("%s - client connected.", getNetAddressString()); } } //--------------------------------------------------------------------------------- TestNetInterface::TestNetInterface(TestGame *theGame, bool server, const Address &bindAddress, const Address &pingAddr) : NetInterface(bindAddress) { game = theGame; isServer = server; pingingServers = !server; lastPingTime = 0; pingAddress = pingAddr; } void TestNetInterface::sendPing() { PacketStream writeStream; // the ping packet right now just has one byte for packet type writeStream.write(U8(GamePingRequest)); writeStream.sendto(mSocket, pingAddress); logprintf("%s - sending ping.", pingAddress.toString()); } void TestNetInterface::tick() { U32 currentTime = Platform::getRealMilliseconds(); if(pingingServers && (lastPingTime + PingDelayTime < currentTime)) { lastPingTime = currentTime; sendPing(); } checkIncomingPackets(); processConnections(); } // handleInfoPacket for the test game is very simple - if this instance // is a client, it pings for servers until one is found to connect to. // If this instance is a server, it responds to ping packets from clients. // More complicated games could maintain server lists, request game information // and more. void TestNetInterface::handleInfoPacket(const Address &address, U8 packetType, BitStream *stream) { PacketStream writeStream; if(packetType == GamePingRequest && isServer) { logprintf("%s - received ping.", address.toString()); // we're a server, and we got a ping packet from a client, // so send back a GamePingResponse to let the client know it // has found a server. writeStream.write(U8(GamePingResponse)); writeStream.sendto(mSocket, address); logprintf("%s - sending ping response.", address.toString()); } else if(packetType == GamePingResponse && pingingServers) { // we were pinging servers and we got a response. Stop the server // pinging, and try to connect to the server. logprintf("%s - received ping response.", address.toString()); TestConnection *connection = new TestConnection; connection->connect(this, address); // connect to the server through the game's network interface logprintf("Connecting to server: %s", address.toString()); pingingServers = false; } } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- TestGame::TestGame(bool server, const Address &interfaceBindAddr, const Address &pingAddress) { isServer = server; myNetInterface = new TestNetInterface(this, isServer, interfaceBindAddr, pingAddress); AsymmetricKey *theKey = new AsymmetricKey(32); myNetInterface->setPrivateKey(theKey); myNetInterface->setRequiresKeyExchange(true); lastTime = Platform::getRealMilliseconds(); if(isServer) { // generate some buildings and AIs: for(S32 i = 0; i < 50; i ++) { Building *building = new Building; building->setInterface(myNetInterface); building->addToGame(this); } for(S32 i = 0; i < 15; i ++) { Player *aiPlayer = new Player(Player::PlayerTypeAI); aiPlayer->setInterface(myNetInterface); aiPlayer->addToGame(this); } serverPlayer = new Player(Player::PlayerTypeMyClient); serverPlayer->addToGame(this); } logprintf("Created a %s...", (server ? "server" : "client")); } TestGame::~TestGame() { delete myNetInterface; for(S32 i = 0; i < buildings.size(); i++) delete buildings[i]; for(S32 i = 0; i < players.size(); i++) delete players[i]; logprintf("Destroyed a %s...", (this->isServer ? "server" : "client")); } void TestGame::createLocalConnection(TestGame *serverGame) { myNetInterface->pingingServers = false; TestConnection *theConnection = new TestConnection; theConnection->connectLocal(myNetInterface, serverGame->myNetInterface); } void TestGame::tick() { U32 currentTime = Platform::getRealMilliseconds(); if(currentTime == lastTime) return; F32 timeDelta = (currentTime - lastTime) / 1000.0f; for(S32 i = 0; i < players.size(); i++) players[i]->update(timeDelta); myNetInterface->tick(); lastTime = currentTime; } void TestGame::moveMyPlayerTo(Position newPosition) { if(isServer) { serverPlayer->serverSetPosition(serverPlayer->renderPos, newPosition, 0, 0.2f); } else if(!myNetInterface->connectionToServer.isNull()) { logprintf("posting new position (%g, %g) to server", newPosition.x, newPosition.y); if(!clientPlayer.isNull()) clientPlayer->rpcPlayerWillMove("Whee! Foo!"); myNetInterface->connectionToServer->rpcSetPlayerPos(newPosition.x, newPosition.y); } } }; --- NEW FILE: Makefile --- # TNL Makefile # (c) 2003 GarageGames # # This makefile is for Linux atm. # # Configuration # CC=g++ -g #-O2 OBJECTS_SERVER=\ testGame.o\ server.o OBJECTS_CLIENT=\ testGame.o\ client.o OBJECTS_WXGUI=\ testGame.o\ testWindow.o \ testGameRender.o \ CFLAGS= .cpp.o : $(CC) -c $(CFLAGS) $< default: $(OBJECTS_SERVER) $(OBJECTS_CLIENT) @echo Building linux dedicated server... $(CC) -o server $(OBJECTS_SERVER) ../tnl/libtnl.a ../libtomcrypt/libtomcrypt.a -lstdc++ -lm $(CC) -o client $(OBJECTS_CLIENT) ../tnl/libtnl.a ../libtomcrypt/libtomcrypt.a -lstdc++ -lm wxgui: CFLAGS += `wx-config --cxxflags` wxgui: $(OBJECTS_WXGUI) @echo Building WX gui... $(CC) -o wxgui $(OBJECTS_WXGUI) ../tnl/libtnl.a ../libtomcrypt/libtomcrypt.a `wx-config --libs --gl-libs ` -lGL clean: rm -f $(OBJECTS_SERVER) $(OBJECTS_CLIENT) $(OBJECTS_WXGUI) server client wxgui --- NEW FILE: testGameRender.cpp --- //----------------------------------------------------------------------------------- // // Torque Network Library - TNLTest example program // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #if defined(_WIN32) #include <windows.h> #include <gl/gl.h> #elif defined (__APPLE__) #include <OpenGL/gl.h> #else #include <GL/gl.h> #endif #include "testGame.h" #include <math.h> namespace TNLTest { void TestGame::renderFrame(int width, int height) { glClearColor(1, 1, 0, 0); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 1, 1, 0, 0, 1); glMatrixMode(GL_MODELVIEW); // first, render the alpha blended circle around the player, // to show the scoping range. if(clientPlayer) { Position p = clientPlayer->renderPos; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_POLYGON); glColor4f(0.5f, 0.5f, 0.5f, 0.65f); for(F32 r = 0; r < 3.1415 * 2; r += 0.1f) { glVertex2f(p.x + 0.25f * cos(r), p.y + 0.25f * sin(r)); } glEnd(); glDisable(GL_BLEND); } // then draw all the buildings. for(S32 i = 0; i < buildings.size(); i++) { Building *b = buildings[i]; glBegin(GL_POLYGON); glColor3f(1, 0, 0); glVertex2f(b->upperLeft.x, b->upperLeft.y); glVertex2f(b->lowerRight.x, b->upperLeft.y); glVertex2f(b->lowerRight.x, b->lowerRight.y); glVertex2f(b->upperLeft.x, b->lowerRight.y); glEnd(); } // last, draw all the players in the game. for(S32 i = 0; i < players.size(); i++) { Player *p = players[i]; glBegin(GL_POLYGON); glColor3f(0,0,0); glVertex2f(p->renderPos.x - 0.012f, p->renderPos.y - 0.012f); glVertex2f(p->renderPos.x + 0.012f, p->renderPos.y - 0.012f); glVertex2f(p->renderPos.x + 0.012f, p->renderPos.y + 0.012f); glVertex2f(p->renderPos.x - 0.012f, p->renderPos.y + 0.012f); glEnd(); glBegin(GL_POLYGON); switch(p->myPlayerType) { case Player::PlayerTypeAI: case Player::PlayerTypeAIDummy: glColor3f(0, 0, 1); break; case Player::PlayerTypeClient: glColor3f(0.5, 0.5, 1); break; case Player::PlayerTypeMyClient: glColor3f(1, 1, 1); break; } glVertex2f(p->renderPos.x - 0.01f, p->renderPos.y - 0.01f); glVertex2f(p->renderPos.x + 0.01f, p->renderPos.y - 0.01f); glVertex2f(p->renderPos.x + 0.01f, p->renderPos.y + 0.01f); glVertex2f(p->renderPos.x - 0.01f, p->renderPos.y + 0.01f); glEnd(); } } }; --- NEW FILE: TNLTest.dsp --- # Microsoft Developer Studio Project File - Name="TNLTest" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=TNLTest - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "TNLTest.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "TNLTest.mak" CFG="TNLTest - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "TNLTest - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "TNLTest - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "TNLTest - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W4 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /YX /FD /c # ADD CPP /nologo /MD /W3 /GR /O2 /I "../wxWindows/include" /I "..\wxWindows\lib\msw" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /D "_MT" /D wxUSE_GUI=1 /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 # ADD BASE RSC /l 0x409 /i "../../include" /d "NDEBUG" # ADD RSC /l 0x409 /i "../wxWindows/include" /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 ../glut/glut32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"LIBC" /out:"../exe/TNLTest.exe" /libpath:"../lib" # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "TNLTest - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W4 /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /YX /FD /c # ADD CPP /nologo /MDd /W3 /GR /Zi /Od /I "../wxWindows/include" /I "..\wxWindows\lib\msw" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /D "_MT" /D wxUSE_GUI=1 /YX /FD /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 # ADD BASE RSC /l 0x409 /i "../../include" /d "_DEBUG" # ADD RSC /l 0x409 /i "../wxWindows/include" /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 tnld.lib libtomcryptd.lib ../glut/glut32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"LIBCD" /out:"../exe/TNLTestD.exe" /pdbtype:sept /libpath:"../lib" # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "TNLTest - Win32 Release" # Name "TNLTest - Win32 Debug" # Begin Source File SOURCE=.\testGame.cpp # End Source File # Begin Source File SOURCE=.\testGame.h # End Source File # Begin Source File SOURCE=.\testGameRender.cpp # End Source File # Begin Source File SOURCE=.\testWindow.cpp # End Source File # End Target # End Project --- NEW FILE: test.rc --- wxSTD_FRAME ICON "mondrian.ico" #include "wx/msw/wx.rc" --- NEW FILE: testGame.h --- //----------------------------------------------------------------------------------- // // Torque Network Library - TNLTest example program // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #include "../tnl/tnl.h" #include "../tnl/tnlNetBase.h" #include "../tnl/tnlGhostConnection.h" #include "../tnl/tnlNetInterface.h" #include "../tnl/tnlNetObject.h" #include "../tnl/tnlLog.h" #include "../tnl/tnlRPC.h" using namespace TNL; /// TNL Graphical Test Application. /// /// The TNLTest application demonstrates some of the more useful /// features of the Torque Network Library. The application presents /// a single window, representing an abstract simulation area. Red /// rectangles are used to represent "buildings" that small squares /// representing players move over. /// /// On the linux and Win32 platforms, TNLTest uses the wxWindows user /// interface API. On Mac OSX TNLTest uses the native Cocoa application /// framework to render. /// /// Each instance of TNLTest can run in one of four modes: as a server, /// able to host multiple clients; as a client, which searches for and then /// connects to a server for game data; a client pinging the localhost for /// connecting to a server on the local machine, and as a combined server /// and client. namespace TNLTest { class TestGame; /// Position structure used for positions of objects in the test game. struct Position { F32 x; ///< X position of the object, from 0 ... 1 F32 y; ///< Y position of the object, from 0 ... 1 }; /// The Player is an example of an object that can move around and update its /// position to clients in the system. Each client in the server controls /// a Player instance constructed for it upon joining the game. /// Client users click on the game window, which generates an event to the server to /// move that client's player to the desired point. class Player : public NetObject { typedef NetObject Parent; public: Position startPos, endPos; ///< All players move along a line from startPos to endPos. Position renderPos; ///< Position at which to render the player - computed during update(). F32 t; ///< Parameter of how far the player is along the line from startPos to endPos. F32 tDelta; ///< Change in t per second (ie, velocity). TestGame *game; ///< The game object this player is associated with /// Enumeration of possible player types in the game. enum PlayerType { PlayerTypeAI, ///< The player is an AI controlled player on the server. PlayerTypeAIDummy, ///< The player is the ghost of an AI controlled player on the server. PlayerTypeClient, ///< The player is owned by a client. If this is a ghost, it is owned by a user other than this client. PlayerTypeMyClient, ///< The player controlled by this client. }; PlayerType myPlayerType; ///< What type of player is this? /// Player constructor, assigns a random position in the playing field to the player, and /// if it is AI controlled will pick a first destination point. Player(PlayerType pt = PlayerTypeClient); /// Player destructor removes the player from the game. ~Player(); /// Mask bits used for determining which object states need to be updated /// to all the clients. enum MaskBits { InitialMask = (1 << 0), ///< This mask bit is never set explicitly, so it can be used for initialization data. PositionMask = (1 << 1), ///< This mask bit is set when the position information changes on the server. }; /// performScopeQuery is called to determine which objects are "in scope" for the client /// that controls this Player instance. In the TNLTest program, all objects in a circle /// of radius 0.25 around the scope object are considered to be in scope. void performScopeQuery(GhostConnection *connection); /// packUpdate writes the Player's ghost update from the server to the client. /// This function takes advantage of the fact that the InitialMask is only ever set /// for the first update of the object to send once-only state to the client. U32 packUpdate(GhostConnection *connection, U32 updateMask, BitStream *stream); /// unpackUpdate reads the data the server wrote in packUpdate from the packet. void unpackUpdate(GhostConnection *connection, BitStream *stream); /// serverSetPosition is called on the server when it receives notice from a client /// to change the position of the player it controls. serverSetPosition will call /// setMaskBits(PositionMask) to notify the network system that this object has /// changed state. void serverSetPosition(Position startPos, Position endPos, F32 t, F32 tDelta); /// Move this object along its path. /// /// If it hits the end point, and it's an AI, it will generate a new destination. void update(F32 timeDelta); /// onGhostAvailable is called on the server when it knows that this Player has been /// constructed on the specified client as a result of being "in scope". In TNLTest /// this method call is used to test the per-ghost targeted RPC functionality of /// NetObject subclasses by calling rpcPlayerIsInScope on the Player ghost on /// the specified connection. void onGhostAvailable(GhostConnection *theConnection); /// onGhostAdd is called for every NetObject on the client after the ghost has /// been constructed and its initial unpackUpdate is called. A return value of /// false from this function would indicate than an error had occured, notifying the /// network system that the connection should be terminated. bool onGhostAdd(GhostConnection *theConnection); /// Adds this Player to the list of Player instances in the specified game. void addToGame(TestGame *theGame); /// rpcPlayerWillMove is used in TNLTest to demonstrate ghost->parent NetObject RPCs. TNL_DECLARE_RPC(rpcPlayerWillMove, (const char *testString)); /// rpcPlayerDidMove is used in TNLTest to demostrate a broadcast RPC from the server /// to all of the ghosts on clients scoping this object. It also demonstrates the /// usage of the Float<> template argument, in this case using 6 bits for each X and /// Y position. Float arguments to RPCs are between 0 and 1. TNL_DECLARE_RPC(rpcPlayerDidMove, (Float<6> x, Float<6> y)); /// rpcPlayerIsInScope is the RPC method called by onGhostAvailable to demonstrate /// targeted NetObject RPCs. onGhostAvailable uses the TNL_RPC_CONSTRUCT_NETEVENT /// macro to construct a NetEvent from the RPC invocation and then posts it only /// to the client for which the object just came into scope. TNL_DECLARE_RPC(rpcPlayerIsInScope, (Float<6> x, Float<6> y)); /// This macro invocation declares the Player class to be known /// to the TNL class management system. TNL_DECLARE_CLASS(Player); }; /// The Building class is an example of a NetObject that is ScopeAlways. /// ScopeAlways objects are transmitted to all clients that are currently /// ghosting, regardless of whether or not the scope object calls GhostConnection::objectInScope /// for them or not. The "buildings" are represented by red rectangles on the /// playing field, and are constructed with random position and extents. class Building : public NetObject { typedef NetObject Parent; public: /// Mask bits used to determine what states are out of date for this /// object and what then represent. enum MaskBits { InitialMask = (1 << 0), ///< Building's only mask bit is the initial mask, as no other states are ever set. }; TestGame *game; ///< The game object this building is associated with Position upperLeft; ///< Upper left corner of the building rectangle on the screen. Position lowerRight; ///< Lower right corner of the building rectangle on the screen. /// The Building constructor creates a random position and extent for the building, and marks it as scopeAlways. Building(); /// The Building destructor removes the Building from the game, if it is associated with a game object. ~Building(); /// Called on the client when this Building object has been ghosted to the /// client and its first unpackUpdate has been called. onGhostAdd adds /// the building to the client game. bool onGhostAdd(GhostConnection *theConnection); /// addToGame is a helper function called by onGhostAdd and on the server /// to add the building to the specified game. void addToGame(TestGame *game); /// packUpdate is called on the server Building object to update any out-of-date network /// state information to the client. Since the Building object only declares an initial /// mask bit state and never calls setMaskBits, this method will only be invoked when /// the ghost is being created on the client. U32 packUpdate(GhostConnection *connection, U32 updateMask, BitStream *stream); /// Reads the update information about the building from the specified packet BitStream. void unpackUpdate(GhostConnection *connection, BitStream *stream); /// This macro declares Building to be a part of the TNL network class management system. TNL_DECLARE_CLASS(Building); }; /// TestConnection is the TNLTest connection class. /// /// The TestConnection class overrides particular methods for /// connection housekeeping, and for connected clients manages /// the transmission of client inputs to the server for player /// position updates. /// /// When a client's connection to the server is lost, the /// TestConnection notifies the network interface that it should begin /// searching for a new server to join. class TestConnection : public GhostConnection { typedef GhostConnection Parent; public: /// The TestConnection constructor. This method contains a line that can be /// uncommented to put the TestConnection into adaptive communications mode. TestConnection(); /// The player object associated with this connection. SafePtr<Player> myPlayer; /// onTimedOut is called when the connection to the server or client has been /// terminated due to a lack of network traffic from the remote host. /// When a TestConnection to a server times out, the client's network interface /// is notified so it can begin searching for another server to connect to. void onTimedOut(); /// onConnectTimedOut is called when the connection request to the server /// is unable to complete due to connection timeout. /// When a TestConnection connect request to a server times out, the client's network interface /// is notified so it can begin searching for another server to connect to. void onConnectTimedOut(); /// onDisconnect is called when the remote host has terminated the connection. /// When a TestConnection to a server is disconnected, the client's network interface /// is notified so it can begin searching for another server to connect to. void onDisconnect(const char *reason); /// onConnectionEstablished is called on both ends of a connection when the connection is established. /// On the server this will create a player for this client, and set it as the client's /// scope object. On both sides this will set the proper ghosting behavior for the connection (ie server to client). void onConnectionEstablished(bool isInitiator); /// Remote function that client calls to set the position of the player on the server. TNL_DECLARE_RPC(rpcSetPlayerPos, (F32 x, F32 y)); /// Enumeration constant used to specify the size field of Float<> parameters in the rpcGotPlayerPos method. enum RPCEnumerationValues { PlayerPosReplyBitSize = 6, ///< Size, in bits of the floats the server sends to the client to acknowledge position updates. }; /// Remote function the server sends back to the client when it gets a player position. /// We only use 6 bits of precision on each float to illustrate the float compression /// that's possible using TNL's RPC. This RPC also sends bool and StringTableEntry data, /// as well as showing the use of the TNL_DECLARE_RPC_ENUM macro. TNL_DECLARE_RPC(rpcGotPlayerPos, (bool b1, bool b2, StringTableEntry string, Float<PlayerPosReplyBitSize> x, Float<PlayerPosReplyBitSize> y)); /// TNL_DECLARE_NETCONNECTION is used to declare that TestConnection is a valid connection class to the /// TNL network system. TNL_DECLARE_NETCONNECTION(TestConnection); }; /// TestNetInterface - the NetInterface subclass used in TNLTest. /// TestNetInterface subclasses TNLTest to provide server pinging and response /// info packets. /// When a client TestNetInterface starts, it begins sending ping /// packets to the pingAddr address specified in the constructor. When a server /// receives a GamePingRequest packet, it sends a GamePingResponse packet to the /// source address of the GamePingRequest. Upon receipt of this response packet, /// the client attempts to connect to the server that returned the response. /// When the connection or connection attempt to that server is terminated, the /// TestNetInterface resumes pinging for available TNLTest servers. class TestNetInterface : public NetInterface { typedef NetInterface Parent; public: /// Constants used in this NetInterface enum Constants { PingDelayTime = 2000, ///< Milliseconds to wait between sending GamePingRequest packets. GamePingRequest = FirstValidInfoPacketId, ///< Byte value of the first byte of a GamePingRequest packet. GamePingResponse, ///< Byte value of the first byte of a GamePingResponse packet. }; bool pingingServers; ///< True if this is a client that is pinging for active servers. U32 lastPingTime; ///< The last platform time that a GamePingRequest was sent from this network interface. bool isServer; ///< True if this network interface is a server, false if it is a client. SafePtr<TestConnection> connectionToServer; ///< A safe pointer to the current connection to the server, if this is a client. Address pingAddress; ///< Network address to ping in searching for a server. This can be a specific host address or it can be a LAN broadcast address. TestGame *game; ///< The game object associated with this network interface. /// Constructor for this network interface, called from the constructor for TestGame. /// The constructor initializes and binds the network interface, as well as sets /// parameters for the associated game and whether or not it is a server. TestNetInterface(TestGame *theGame, bool server, const Address &bindAddress, const Address &pingAddr); /// handleInfoPacket overrides the method in the NetInterface class to handle processing /// of the GamePingRequest and GamePingResponse packet types. void handleInfoPacket(const Address &address, U8 packetType, BitStream *stream); /// sendPing sends a GamePingRequest packet to pingAddress of this TestNetInterface. void sendPing(); /// Tick checks to see if it is an appropriate time to send a ping packet, in addition /// to checking for incoming packets and processing connections. void tick(); }; /// The TestGame class manages a TNLTest client or server instance. /// TestGame maintains a list of all the Player and Building objects in the /// playing area, and interfaces with the specific platform's windowing /// system to respond to user input and render the current game display. class TestGame { public: Vector<Player *> players; ///< vector of player objects in the game Vector<Building *> buildings; ///< vector of buildings in the game bool isServer; ///< was this game created to be a server? TestNetInterface *myNetInterface; ///< network interface for this game U32 lastTime; ///< last time that tick() was called SafePtr<Player> serverPlayer; ///< the player that the server controls SafePtr<Player> clientPlayer; ///< the player that this client controls, if this game is a client /// Constructor for TestGame, determines whether this game will be a client /// or a server, and what addresses to bind to and ping. If this game is a /// server, it will construct 50 random buildings and 15 random AI players to /// populate the "world" with. TestGame also constructs an AsymmetricKey to /// demonstrate establishing secure connections with clients and servers. TestGame(bool server, const Address &bindAddress, const Address &pingAddress); /// Destroys a game, freeing all Player and Building objects associated with it. ~TestGame(); /// createLocalConnection demonstrates the use of so-called "short circuit" connections /// for connecting to a server NetInterface in the same process as this client game. void createLocalConnection(TestGame *serverGame); /// Called periodically by the platform windowing code, tick will update /// all the players in the simulation as well as tick() the game's /// network interface. void tick(); /// renderFrame is called by the platform windowing code to notify the game /// that it should render the current world using the specified window area. void renderFrame(int width, int height); /// moveMyPlayerTo is called by the platform windowing code in response to /// user input. void moveMyPlayerTo(Position newPosition); }; /// The instance of the client game, if there is a client currently running. extern TestGame *clientGame; /// The instance of the server game, if there is a server currently running. extern TestGame *serverGame; }; using namespace TNLTest; --- NEW FILE: server.cpp --- //----------------------------------------------------------------------------------- // // Torque Network Library - TNLTest dedicated server and unit tests // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #include "testGame.h" #include <stdio.h> #include "../tnl/tnlSymmetricCipher.h" #include "../tnl/tnlAsymmetricKey.h" #include "../tnl/tnlCertificate.h" #include "../tnl/tnlBitStream.h" #define TNLTest_SERVER //#define TNL_TEST_RPC //#define TNL_TEST_PROTOCOL //#define TNL_TEST_RPC //#define TNL_TEST_NETINTERFACE //#define TNL_TEST_CERTIFICATE // server.cpp // // This file contains several different test programs, one of which // can be enabled by uncommenting one of the previous #define'd macros. // // All of the test programs use the following LogConsumer to write // output from the TNL to the standard output console. class DedicatedServerLogConsumer : public LogConsumer { public: void logString(const char *string) { printf("%s\n", string); } } gDedicatedServerLogConsumer; #if defined(TNLTest_SERVER) // The TNLTest_SERVER test is an example dedicated server for the // TNLTest application. int main(int argc, const char **argv) { const char *localBroadcastAddress = "IP:broadcast:28999"; S32 port = 28999; if(argc == 2) port = atoi(argv[1]); serverGame = new TestGame(true, Address(IPProtocol, Address::Any, port), Address(localBroadcastAddress)); for(;;) serverGame->tick(); return 0; } #elif defined(TNL_TEST_RPC) // The TNL_TEST_RPC exercises the RPC functionality of TNL. The RPC // code is the only code that is challenging to port to new platforms // because of its reliance on platform specific stack structure and // function invocation guidelines. // // All RPC methods are declared to be virtual and are invoked using a // virtual member function pointer. The assembly language output of the // VFTester calls below can be used to determine how to invoke virtual member // functions on different platforms and compilers. class RPCTestConnection : public EventConnection { public: /// Test RPC function for long argument lists, especially useful in debugging architectures that pass some arguments in registers, and some on the stack. TNL_DECLARE_RPC(rpcLongArgChainTest, (Float<7> i1, Float<6> i2, F32 i3, F32 i4, F32 i5, F32 i6, F32 i7, F32 i8, F32 i9, F32 i10, F32 i11, F32 i12, F32 i13, F32 i14, F32 i15, U32 i16, U32 i17, F32 i18, F32 i19, F32 i20, F32 i21, F32 i22)); TNL_DECLARE_RPC(rpcVectorTest, (IPAddressRef theIP, ByteBufferRef buf, const Vector<IPAddress> &v0, const Vector<const char *> &v1, const Vector<F32> &v2)); TNL_DECLARE_RPC(rpcSTETest, (StringTableEntry e1, const Vector<StringTableEntry> &v2)); }; TNL_IMPLEMENT_RPC(RPCTestConnection, rpcLongArgChainTest, (Float<7> i1, Float<6> i2, F32 i3, F32 i4, F32 i5, F32 i6, F32 i7, F32 i8, F32 i9, F32 i10, F32 i11, F32 i12, F32 i13, F32 i14, F32 i15, U32 i16, U32 i17, F32 i18, F32 i19, F32 i20, F32 i21, F32 i22), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCDirAny, 0) { F32 fi1 = i1; F32 fi2 = i2; logprintf( "%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %d %d %g %g %g %g %g", fi1, fi2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20, i21, i22); } TNL_IMPLEMENT_RPC(RPCTestConnection, rpcVectorTest, (IPAddressRef theIP, ByteBufferRef buf, const Vector<IPAddress> &v0, const Vector<const char *> &v1, const Vector<F32> &v2), NetClassGroupGameMask, RPCGuaranteedOrdered, RPCDirAny, 0) { // The assembly output of the following lines is used to determine // how the RPC executor should dispatch RPC calls. VFTester *newTester = new VFTester; fptr thePtr = &VFTester::doSomething; (newTester->*thePtr)(50); logprintf("Vector 0 size: %d, Vector 1 si... [truncated message content] |