// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // // Includes // #include "nel/misc/types_nl.h" #include "nel/misc/debug.h" #include "nel/misc/mem_stream.h" #include "nel/misc/time_nl.h" #include "nel/misc/config_file.h" #include "nel/net/udp_sock.h" #include "nel/net/callback_client.h" #include "nel/net/inet_address.h" #include "nel/net/udp_sim_sock.h" #include "graph.h" #ifdef USE_3D #include "nel/3d/u_driver.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_camera.h" #include "nel/3d/u_instance.h" #include "nel/3d/u_animation_set.h" #include "nel/3d/u_play_list.h" #include "nel/3d/u_play_list_manager.h" #include "nel/3d/u_text_context.h" #include "nel/3d/u_texture.h" #include "nel/3d/event_mouse_listener.h" using namespace NL3D; #endif #ifndef UDP_DIR #define UDP_DIR "." #endif // UDP_DIR // // Namespaces // using namespace std; using namespace NLMISC; using namespace NLNET; // // Variables // // must be increase at each version and must be the same value as the server uint32 Version = 2; string ServerAddr = "itsalive.nevrax.org"; // ldserver uint16 UDPPort = 45455; uint16 TCPPort = 45456; uint32 MaxUDPPacketSize = 1000; CUdpSimSock *UdpSock = NULL; uint8 Mode = 0; uint64 Session = 0; string ConnectionName; CConfigFile ConfigFile; #ifdef USE_3D CGraph FpsGraph ("frame rate (fps)", 10.0f, 110.0f, 100.0f, 100.0f, CRGBA(128,0,0,128), 1000, 150.0f); CGraph DownloadGraph ("download (bps)", 10.0f, 260.0f, 100.0f, 100.0f, CRGBA(0,0,128,128), 1000, 20000.0f); CGraph UploadGraph ("upload (bps)", 10.0f, 360.0f, 100.0f, 100.0f, CRGBA(0,128,128,128), 1000, 20000.0f); CGraph LagGraph ("lag (ms)", 150.0f, 110.0f, 100.0f, 100.0f, CRGBA(128,64,64,128), 100000, 2000.0f); #endif // // Functions // void exit (const string &reason) { if (!reason.empty()) InfoLog->displayRawNL ("%s", reason.c_str()); InfoLog->displayRawNL ("Press <enter> to exit"); getchar (); exit(EXIT_FAILURE); } // // Config file functions // void createConfigFile() { FILE *fp = fopen ("client.cfg", "wt"); if (fp == NULL) { InfoLog->displayRawNL ("Can't create client.cfg"); } else { fprintf (fp, "ServerAddress = \"%s\";\n", ServerAddr.c_str()); fprintf (fp, "SimInLag = 0;\n"); fprintf (fp, "SimInPacketLost = 0;\n"); fprintf (fp, "SimOutLag = 0;\n"); fprintf (fp, "SimOutPacketLost = 0;\n"); fprintf (fp, "SimOutPacketDuplication = 0;\n"); fprintf (fp, "SimOutPacketDisordering = 0;\n"); fprintf (fp, "ConnectionName = \"\";\n"); fclose (fp); } } void checkConnectionName () { if (ConnectionName.size() > 30) { exit ("Bad connection name (must be <= 30 characters)"); } if (ConnectionName.size() > 0 && ConnectionName[ConnectionName.size()-1] == '\n') { ConnectionName = ConnectionName.substr (0, ConnectionName.size()-1); } if (ConnectionName.size() <= 0) { exit ("Bad connection name (must be > 0 character)"); } for (uint i = 0; i < ConnectionName.size(); i++) { if (!isalnum(ConnectionName[i])) { exit ("Bad connection name, only alpha numeric characters is allowed (char '%c' is not alphanum)"); } } } void loadConfigFile () { FILE *fp = fopen ("client.cfg", "rt"); if (fp == NULL) { createConfigFile(); } else { fclose (fp); } ConfigFile.load ("client.cfg"); // set internet simulation values CUdpSimSock::setSimValues (ConfigFile); ServerAddr = ConfigFile.getVar("ServerAddress").asString(); ConnectionName = ConfigFile.getVar("ConnectionName").asString(); if (ConnectionName.empty()) { InfoLog->displayRawNL ("Please, enter a connection name"); InfoLog->displayRawNL ("(only alphanumeric character limited to 30 character, no space)"); InfoLog->displayRawNL ("For example enter your name and/or your location (ie: \"AceHome\"),"); InfoLog->displayRawNL ("It'll be use to find your stat file easier:"); char cn[128]; if (fgets (cn, 127, stdin) == NULL) { exit ("Error during the keyboard scanning"); } ConnectionName = cn; checkConnectionName (); ConfigFile.getVar ("ConnectionName").setAsString(ConnectionName); ConfigFile.save (); } else { checkConnectionName (); } } // // Callbacks // void cbInfo (CMessage &msgin, TSockId from, CCallbackNetBase &netbase) { string line; msgin.serial (line); InfoLog->displayRawNL ("%s", line.c_str()); #ifdef USE_3D string token = "MeanPongTime "; string::size_type pos=line.find (token); string::size_type pos2=line.find (" ", pos+token.size()); float val; NLMISC::fromString(line.substr (pos+token.size(), pos2-pos-token.size()), val); LagGraph.addOneValue (val); #endif } void cbInit (CMessage &msgin, TSockId from, CCallbackNetBase &netbase) { msgin.serial (Session); // create the UDP connection nlassert (UdpSock == NULL); UdpSock = new CUdpSimSock( false ); try { UdpSock->connect( CInetAddress (ServerAddr, UDPPort) ); } catch ( Exception& e ) { InfoLog->displayRawNL ("Cannot connect to remote UDP host '%s': %s", ServerAddr.c_str(), e.what() ); exit (""); } } void cbStart (CMessage &msgin, TSockId from, CCallbackNetBase &netbase) { InfoLog->displayRawNL ("Bench is starting.."); Mode = 1; } void cbDisconnect (TSockId from, void *arg) { exit ("Lost the server connection. You should not have the last client version\nGet it here: http://www.nevrax.org/download/bench.zip"); } TCallbackItem CallbackArray[] = { { "INIT", cbInit }, { "INFO", cbInfo }, { "START", cbStart }, }; // // Main // int main( int argc, char **argv ) { createDebug (); DebugLog->addNegativeFilter(" "); InfoLog->displayRawNL ("\nNevrax UDP benchmark client\n\nPress <CTRL-C> to exit"); CPath::addSearchPath(UDP_DIR); loadConfigFile (); CCallbackClient *cc = new CCallbackClient; cc->addCallbackArray (CallbackArray, sizeof(CallbackArray)/sizeof(CallbackArray[0])); cc->setDisconnectionCallback (cbDisconnect, NULL); try { InfoLog->displayRawNL ("Try to connect to %s:%d", ServerAddr.c_str(), TCPPort); cc->connect(CInetAddress (ServerAddr, TCPPort)); CMessage msgout ("INIT"); msgout.serial (ConnectionName); msgout.serial (Version); cc->send (msgout); InfoLog->displayRawNL ("Waiting the server answer..."); } catch(Exception &e) { InfoLog->displayRawNL ("Can't connect to %s:%d (%s)\n", ServerAddr.c_str(), TCPPort, e.what()); exit (""); } uint8 *packet = new uint8[MaxUDPPacketSize]; uint32 psize; #ifdef USE_3D UDriver *Driver = UDriver::createDriver(); Driver->setDisplay(UDriver::CMode(800, 600, 32, true)); UScene *Scene= Driver->createScene(false); UCamera Camera= Scene->getCam(); Camera.setTransformMode(UTransform::DirectMatrix); UTextContext *TextContext= Driver->createTextContext(CPath::lookup("n019003l.pfb")); TextContext->setFontSize(18); Camera.setPerspective(80*(float)Pi/180, 1.33f, 0.15f, 1000); CEvent3dMouseListener MouseListener; MouseListener.addToServer(Driver->EventServer); MouseListener.setFrustrum(Camera.getFrustum()); MouseListener.setHotSpot(CVector(0,0,0)); CMatrix initMat; initMat.setPos(CVector(0,-5,0)); MouseListener.setMatrix(initMat); #endif while (cc->connected ()) { #ifdef USE_3D // Manip. Camera.setMatrix(MouseListener.getViewMatrix()); Driver->EventServer.pump(); if(Driver->AsyncListener.isKeyPushed(KeyESCAPE)) return EXIT_SUCCESS; Driver->clearBuffers(CRGBA(255,255,255,0)); Scene->render(); CGraph::render (*Driver, *TextContext); Driver->swapBuffers(); FpsGraph.addValue (1); #endif CConfigFile::checkConfigFiles (); // update TCP connection cc->update (); // update UDP connection if (UdpSock != NULL) { if (Mode == 0) { // init the UDP connection CMemStream msgout; msgout.serial (Mode); msgout.serial (Session); uint32 size = msgout.length(); #ifdef USE_3D UploadGraph.addValue ((float)size); #endif UdpSock->send (msgout.buffer(), size); nldebug ("Sent init udp connection"); nlSleep (100); } while (UdpSock->dataAvailable()) { psize = MaxUDPPacketSize; UdpSock->receive (packet, psize); #ifdef USE_3D DownloadGraph.addValue ((float)psize); #endif CMemStream msgin( true ); memcpy (msgin.bufferToFill (psize), packet, psize); sint64 t = 0; msgin.serial (t); uint32 p = 0; msgin.serial (p); uint32 b = 0; msgin.serial (b); // I received a ping, send a pong CMemStream msgout; msgout.serial (Mode); msgout.serial (t); msgout.serial (p); msgout.serial (b); uint8 dummy=0; while (msgout.length() < 200) msgout.serial (dummy); uint32 size = msgout.length(); nlassert (size == 200); #ifdef USE_3D UploadGraph.addValue ((float)size); #endif UdpSock->send (msgout.buffer(), size); } } nlSleep (1); } exit (""); return EXIT_SUCCESS; }