00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <server/ServerBrowserInfo.h>
00022 #include <server/ScorchedServer.h>
00023 #include <server/ServerState.h>
00024 #include <common/OptionsScorched.h>
00025 #include <common/OptionsTransient.h>
00026 #include <common/Defines.h>
00027 #include <net/NetInterface.h>
00028 #include <tank/TankContainer.h>
00029 #include <tank/TankColorGenerator.h>
00030 #include <tank/TankState.h>
00031 #include <tank/TankScore.h>
00032 #include <string.h>
00033
00034 ServerBrowserInfo *ServerBrowserInfo::instance_ = 0;
00035
00036 ServerBrowserInfo *ServerBrowserInfo::instance()
00037 {
00038 if (!instance_)
00039 {
00040 instance_ = new ServerBrowserInfo();
00041 }
00042 return instance_;
00043 }
00044
00045 ServerBrowserInfo::ServerBrowserInfo() : udpsock_(0)
00046 {
00047 packetV_ = SDLNet_AllocPacketV(4, 10000);
00048 packetVOut_ = SDLNet_AllocPacketV(20, 10000);
00049 }
00050
00051 ServerBrowserInfo::~ServerBrowserInfo()
00052 {
00053 }
00054
00055 bool ServerBrowserInfo::start()
00056 {
00057 udpsock_ = SDLNet_UDP_Open(ScorchedServer::instance()->getOptionsGame().getPortNo() + 1);
00058 if(!udpsock_) return false;
00059 return true;
00060 }
00061
00062 void ServerBrowserInfo::processMessages()
00063 {
00064 if (!udpsock_) return;
00065
00066 int numrecv = SDLNet_UDP_RecvV(udpsock_, packetV_);
00067 if(numrecv <=0) return;
00068
00069 int numsent = 0;
00070 for(int i=0; i<numrecv; i++)
00071 {
00072 if (packetV_[i]->len == 0) packetV_[i]->data[0] = '\0';
00073
00074
00075 NetInterface::getBytesIn() += packetV_[i]->len;
00076 NetInterface::getPings() ++;
00077
00078
00079 std::list<std::string> reply;
00080 const char *wrapper = "pong";
00081 if (0 == strncmp((char *) packetV_[i]->data, "status", 6))
00082 {
00083 wrapper = "status";
00084 processStatusMessage(reply);
00085 }
00086 else if (0 == strncmp((char *) packetV_[i]->data, "info", 4))
00087 {
00088 wrapper = "info";
00089 processInfoMessage(reply);
00090 }
00091 else if (0 == strncmp((char *) packetV_[i]->data, "players", 7))
00092 {
00093 wrapper = "players";
00094 processPlayerMessage(reply);
00095 }
00096
00097 bool first = true;
00098 while (!reply.empty() || first)
00099 {
00100 first = false;
00101
00102 std::string buffer = "<";
00103 buffer += wrapper;
00104 buffer += " ";
00105 while (!reply.empty())
00106 {
00107 std::string next = reply.front();
00108 reply.pop_front();
00109 if (strlen(next.c_str()) <= 75)
00110 {
00111 buffer += next;
00112 }
00113
00114 if (strlen(buffer.c_str()) > 800) break;
00115 }
00116 buffer += "/>";
00117
00118 int len = (int) strlen(buffer.c_str())+1;
00119 memcpy(packetVOut_[numsent]->data, buffer.c_str(), len);
00120 packetVOut_[numsent]->len = len;
00121 packetVOut_[numsent]->address.host = packetV_[i]->address.host;
00122 packetVOut_[numsent]->address.port = packetV_[i]->address.port;
00123 packetVOut_[numsent]->channel = packetV_[i]->channel;
00124
00125 NetInterface::getBytesOut() += len;
00126
00127
00128
00129 if (++numsent > 20) break;
00130 }
00131 }
00132
00133
00134 SDLNet_UDP_SendV(udpsock_, packetVOut_, numsent);
00135 }
00136
00137 void ServerBrowserInfo::processStatusMessage(std::list<std::string> &reply)
00138 {
00139 char *serverName = (char *) ScorchedServer::instance()->getOptionsGame().getServerName();
00140 char version[256];
00141 snprintf(version, 256, "%s (%s)", S3D::ScorchedVersion.c_str(), S3D::ScorchedProtocolVersion.c_str());
00142 unsigned currentState = ScorchedServer::instance()->getGameState().getState();
00143 bool started = (currentState != ServerState::ServerStateTooFewPlayers);
00144 char players[25];
00145 snprintf(players, 25, "%i", ScorchedServer::instance()->getTankContainer().getNoOfTanks());
00146 char maxplayers[25];
00147 snprintf(maxplayers, 25, "%i", ScorchedServer::instance()->getOptionsGame().getNoMaxPlayers());
00148 char type[100];
00149 snprintf(type, 100, "%s (%s)",
00150 ScorchedServer::instance()->getOptionsTransient().getGameType(),
00151 ((ScorchedServer::instance()->getOptionsGame().getTeams() > 1)?"Teams":"No Teams"));
00152 bool stats = (0 != strcmp(ScorchedServer::instance()->getOptionsGame().getStatsLogger(), "none"));
00153
00154 int compplayers = 0;
00155 std::map<unsigned int, Tank *> &tanks =
00156 ScorchedServer::instance()->getTankContainer().getPlayingTanks();
00157 std::map<unsigned int, Tank *>::iterator tanksitor;
00158 for (tanksitor = tanks.begin();
00159 tanksitor != tanks.end();
00160 tanksitor++)
00161 {
00162 Tank *tank = (*tanksitor).second;
00163 if (tank->getTankAI()) compplayers++;
00164 }
00165
00166 reply.push_back(addTag("gametype", type));
00167 reply.push_back(addTag("state", (started?"Started":"Waiting")));
00168 reply.push_back(addTag("servername", serverName));
00169 reply.push_back(addTag("fullversion", version));
00170 reply.push_back(addTag("version", S3D::ScorchedVersion));
00171 reply.push_back(addTag("protocolversion", S3D::ScorchedProtocolVersion));
00172 reply.push_back(addTag("mod",
00173 ScorchedServer::instance()->getOptionsGame().getMod()));
00174 reply.push_back(addTag("password",
00175 ScorchedServer::instance()->getOptionsGame().getServerPassword()[0]?"On":"Off"));
00176 reply.push_back(addTag("noplayers", players));
00177 reply.push_back(addTag("maxplayers", maxplayers));
00178 reply.push_back(addTag("compplayers", S3D::formatStringBuffer("%i", compplayers)));
00179 reply.push_back(addTag("stats", (stats?"yes":"no")));
00180 reply.push_back(addTag("round", S3D::formatStringBuffer("%i/%i",
00181 ScorchedServer::instance()->getOptionsTransient().getCurrentRoundNo(),
00182 ScorchedServer::instance()->getOptionsGame().getNoRounds())));
00183 reply.push_back(addTag("os", S3D::getOSDesc()));
00184 }
00185
00186 void ServerBrowserInfo::processInfoMessage(std::list<std::string> &reply)
00187 {
00188
00189
00190 std::list<OptionEntry *> &options = ScorchedServer::instance()->getOptionsGame().
00191 getMainOptions().getOptions();
00192 std::list<OptionEntry *>::iterator optionItor;
00193 for (optionItor = options.begin();
00194 optionItor != options.end();
00195 optionItor ++)
00196 {
00197 OptionEntry *entry = (*optionItor);
00198 if (!(entry->getData() & OptionEntry::DataProtected) &&
00199 !(entry->getData() & OptionEntry::DataDepricated))
00200 {
00201 reply.push_back(
00202 addTag(entry->getName(), entry->getValueAsString()));
00203 }
00204 }
00205 }
00206
00207 void ServerBrowserInfo::processPlayerMessage(std::list<std::string> &reply)
00208 {
00209
00210 char tmp[128];
00211 std::map<unsigned int, Tank *> &tanks =
00212 ScorchedServer::instance()->getTankContainer().getPlayingTanks();
00213 std::map<unsigned int, Tank *>::iterator tankItor;
00214 int i=0;
00215 for (tankItor = tanks.begin();
00216 tankItor != tanks.end();
00217 tankItor++, i++)
00218 {
00219 Tank *tank = (*tankItor).second;
00220
00221 snprintf(tmp, 128, "pn%i", i);
00222 reply.push_back(addTag(tmp,
00223 LangStringUtil::convertFromLang(tank->getTargetName())));
00224
00225 snprintf(tmp, 128, "ps%i", i);
00226 reply.push_back(addTag(tmp, tank->getScore().getScoreString()));
00227
00228 snprintf(tmp, 128, "pt%i", i);
00229 reply.push_back(addTag(tmp, tank->getScore().getTimePlayedString()));
00230
00231 snprintf(tmp, 128, "pm%i", i);
00232 reply.push_back(addTag(tmp, TankColorGenerator::getTeamName(tank->getTeam())));
00233
00234 snprintf(tmp, 128, "pa%i", i);
00235 reply.push_back(addTag(tmp, (tank->getTankAI()?"N":"Y")));
00236
00237 snprintf(tmp, 128, "pr%i", i);
00238 reply.push_back(addTag(tmp, S3D::formatStringBuffer("%i", tank->getScore().getRank())));
00239 }
00240
00241 }
00242
00243 std::string ServerBrowserInfo::addTag(const std::string &name, const std::string &value)
00244 {
00245 std::string content(value), result;
00246 XMLNode::removeSpecialChars(content, result);
00247
00248 return S3D::formatStringBuffer("%s='%s' ", name.c_str(), result.c_str());
00249 }