void leagueOverSeer::URLDone(const char* /*URL*/, const void* data, unsigned int /*size*/, bool /*complete*/) //Everything went fine with the report
{
    std::string siteData = (const char*)(data); //Convert the data to a std::string
    bz_debugMessagef(1, "URL Job Successful! Data returned: %s", siteData.c_str());

    if (strcmp(siteData.substr(0, 6).c_str(), "INSERT") == 0 || strcmp(siteData.substr(0, 6).c_str(), "DELETE") == 0)
        doQuery(siteData);
    else if (strcmp(siteData.c_str(), "<html>") < 0)
    {
        bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "%s", siteData.c_str());
        bz_debugMessagef(DEBUG, "%s", siteData.c_str());
    }
}
sqlite3_stmt* leagueOverSeer::prepareQuery(std::string sql)
{
    /*
        Thanks to blast for this function
    */

    // Search our std::map for this statement
    PreparedStatementMap::iterator itr = preparedStatements.find(sql);

    // If it doesn't exist, create it
    if (itr == preparedStatements.end())
    {
        sqlite3_stmt* newStatement;

        if (sqlite3_prepare_v2(db, sql.c_str(), -1, &newStatement, 0) != SQLITE_OK)
        {
            bz_debugMessagef(2, "DEBUG :: League Over Seer :: SQLite :: Failed to generate prepared statement for '%s' :: Error #%i: %s", sql.c_str(), sqlite3_errcode(db), sqlite3_errmsg(db));
            return NULL;
        }
        else
        {
            preparedStatements[sql] = newStatement;
        }
    }

    return preparedStatements[sql];
}
void leagueOverSeer::doQuery(std::string query)
{
    /*
        Execute a SQL query without the need of any return values
    */

    bz_debugMessage(2, "DEBUG :: League Over Seer :: Executing following SQL query...");
    bz_debugMessagef(2, "DEBUG :: League Over Seer :: %s", query.c_str());

    char* db_err = 0; //a place to store the error
    sqlite3_exec(db, query.c_str(), NULL, 0, &db_err); //execute

    if (db_err != 0) //print out any errors
    {
        bz_debugMessage(2, "DEBUG :: League Over Seer :: SQL ERROR!");
        bz_debugMessagef(2, "DEBUG :: League Over Seer :: %s", db_err);
    }
}
void leagueOverSeer::updateTeamNames(void)
{
    int totaltanks = bz_getTeamCount(eRogueTeam) + bz_getTeamCount(eRedTeam) + bz_getTeamCount(eGreenTeam) + bz_getTeamCount(eBlueTeam) + bz_getTeamCount(ePurpleTeam) + bz_getTeamCount(eObservers);

    if (totaltanks > 0)
        return;

    // Build the POST data for the URL job
    std::string teamNameDump = "query=teamDump";
    bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Updating Team name database...");

    bz_addURLJob(LEAGUE_URL.c_str(), this, teamNameDump.c_str()); //Send the team update request to the league website
}
Example #5
0
void LogDetail::Init ( const char* /*commandLine*/ )
{
  Register(bz_eSlashCommandEvent);
  Register(bz_eRawChatMessageEvent);
  Register(bz_eServerMsgEvent);
  Register(bz_ePlayerJoinEvent);
  Register(bz_ePlayerPartEvent);
  Register(bz_ePlayerAuthEvent);
  Register(bz_eMessageFilteredEvent);

  bz_debugMessage(0, "SERVER-STATUS Running");
  bz_debugMessagef(0, "SERVER-MAPNAME %s", bz_getPublicDescription().c_str());
  listPlayers( join , NULL );
}
void leagueOverSeer::Cleanup (void)
{
    bz_debugMessagef(0, "League Over Seer %i.%i.%i (%i) unloaded.", MAJOR, MINOR, REV, BUILD);

    Flush(); // Clean up all the events

    // Remove the prepared SQLite statement from memory
    sqlite3_finalize(getPlayerMotto);

    // Remove all the slash commands
    bz_removeCustomSlashCommand("official");
    bz_removeCustomSlashCommand("fm");
    bz_removeCustomSlashCommand("finish");
    bz_removeCustomSlashCommand("cancel");
    bz_removeCustomSlashCommand("spawn");
    bz_removeCustomSlashCommand("resume");
    bz_removeCustomSlashCommand("pause");
}
Example #7
0
void HTTPServer::process(bz_EventData *eventData)
{
  if (eventData->eventType == bz_eTickEvent) {
    update();
  } else {
    bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;

    // log out the data if our level is high enough
    if (bz_getDebugLevel() >= 4) {
      char *temp = (char*)malloc(connData->size+1);
      memcpy(temp,connData->data,connData->size);
      temp[connData->size] = 0;
      bz_debugMessagef(4,"Plug-in HTTPServer: Non ProtoConnection connection from %d with %s",connData->connectionID,temp);
      free(temp);
    }

    // we go an accept everyone so that we can see if they are going to give us an HTTP command
    if(bz_registerNonPlayerConnectionHandler(connData->connectionID, this)) {
      // record the connection
      HTTPConnection connection;
      connection.connectionID = connData->connectionID;
      connection.request = eUnknown;
      connection.sessionID = generateSessionID();  // new ID in case they don't have one

      HTTPConnectionMap::iterator itr = liveConnections.find(connection.connectionID);

      if (itr != liveConnections.end())
	liveConnections.erase(itr);	// something weird is happening here

      liveConnections[connection.connectionID] = connection;

      // go and process any data they have and see what the deal is
      pending(connData->connectionID, (char*)connData->data, connData->size);
    }
  }
}
Example #8
0
void HTTPServer::pending(int connectionID, void *d, unsigned int s)
{
  HTTPConnectionMap::iterator itr = liveConnections.find(connectionID);

  if (itr == liveConnections.end())
    return;

  HTTPConnection &connection = itr->second;

  // grab the current data
  if(d && s) {
    char *t = (char*)malloc(s+1);
    memcpy(t,d,s);
    t[s] = 0;
    connection.currentData += t;
    free(t);
  }

  // see what our status is
  if (!connection.request) {
    std::stringstream stream(connection.currentData);

    std::string request, resource, httpVersion;
    stream >> request >> resource >> httpVersion;

    if (request.size() && resource.size() && httpVersion.size()) {
      if (compare_nocase(request,"get") == 0)
	connection.request = eGet;
      else if (compare_nocase(request,"head") == 0)
	connection.request = eHead;
      else if (compare_nocase(request,"post") == 0)
	connection.request = ePost;
      else if (compare_nocase(request,"put") == 0)
	connection.request = ePut;
      else if (compare_nocase(request,"delete") == 0)
	connection.request = eDelete;
      else if (compare_nocase(request,"trace") == 0)
	connection.request = eTrace;
      else if (compare_nocase(request,"options") == 0)
	connection.request = eOptions;
      else if (compare_nocase(request,"connect") == 0)
	connection.request = eConnect;
      else
	connection.request = eOther;

      if (httpVersion != "HTTP/1.1" && httpVersion != "HTTP/1.0")
	bz_debugMessagef(1,"HTTPServer HTTP version of %s requested",httpVersion.c_str());

      if (resource.size() > 1) {
	size_t p = resource.find_first_of('/');
	if (p != std::string::npos) {
	  if (p == 0)
	    p = resource.find_first_of('/',p+1);

	  if (p == std::string::npos) {
	    // there is only one / so the stuff after the slash in the vdir and the resource is NULL
	    connection.vdir.resize(resource.size()-1);
	    std::copy(resource.begin()+1,resource.end(),connection.vdir.begin());
	  } else {
	    connection.vdir.resize(p-1);
	    std::copy(resource.begin()+1,resource.begin()+p,connection.vdir.begin());

	    connection.resource.resize(resource.size()-p-1);
	    std::copy(resource.begin()+p+1,resource.end(),connection.resource.begin());
	  }
	}
      }
    }
  }
Example #9
0
void LogDetail::Event( bz_EventData *eventData )
{
  bz_ChatEventData_V1 *chatData = (bz_ChatEventData_V1 *) eventData;
  bz_ServerMsgEventData_V1 *serverMsgData = (bz_ServerMsgEventData_V1 *) eventData;
  bz_SlashCommandEventData_V1 *cmdData = (bz_SlashCommandEventData_V1 *) eventData;
  bz_PlayerJoinPartEventData_V1 *joinPartData = (bz_PlayerJoinPartEventData_V1 *) eventData;
  bz_PlayerAuthEventData_V1 *authData = (bz_PlayerAuthEventData_V1 *) eventData;
  bz_MessageFilteredEventData_V1 *filteredData = (bz_MessageFilteredEventData_V1 *) eventData;
  char temp[9] = {0};

  if (eventData) {
    switch (eventData->eventType) {
      case bz_eSlashCommandEvent:
	// Slash commands are case insensitive
	// Tokenize the stream and check the first word
	// /report -> MSG-REPORT
	// anything -> MSG-COMMAND

	strncpy(temp, cmdData->message.c_str(), 8);

	if (strcasecmp( temp, "/REPORT ") == 0) {
	  bz_debugMessagef(0, "MSG-REPORT %s %s",
			   displayCallsign( cmdData->from ).c_str(),
			   cmdData->message.c_str()+8);
	} else {
	  bz_debugMessagef(0, "MSG-COMMAND %s %s",
			   displayCallsign( cmdData->from ).c_str(),
			   cmdData->message.c_str()+1);
	}
	break;
      case bz_eRawChatMessageEvent:
	if ((chatData->to == BZ_ALLUSERS) && (chatData->team == eNoTeam)) {
	  bz_debugMessagef(0, "MSG-BROADCAST %s %s",
			   displayCallsign( chatData->from ).c_str(),
			   chatData->message.c_str());
	} else if (chatData->to == BZ_NULLUSER) {
	  if (chatData->team == eAdministrators) {
	    bz_debugMessagef(0, "MSG-ADMIN %s %s",
			     displayCallsign( chatData->from ).c_str(),
			     chatData->message.c_str());
	  } else {
	    bz_debugMessagef(0, "MSG-TEAM %s %s %s",
			     displayCallsign( chatData->from ).c_str(),
			     displayTeam( chatData->team ).c_str(),
			     chatData->message.c_str());
	  }
	} else {
	  bz_debugMessagef(0, "MSG-DIRECT %s %s %s",
			   displayCallsign( chatData->from ).c_str(),
			   displayCallsign( chatData->to ).c_str(),
			   chatData->message.c_str());
	}
	break;
      case bz_eMessageFilteredEvent:
	bz_debugMessagef(0, "MSG-FILTERED %s %s",
			 displayCallsign( filteredData->playerID ).c_str(),
			 filteredData->filteredMessage.c_str());
	break;
      case bz_eServerMsgEvent:
	if ((serverMsgData->to == BZ_ALLUSERS) && (serverMsgData->team == eNoTeam)) {
	  bz_debugMessagef(0, "MSG-BROADCAST 6:SERVER %s",
			   serverMsgData->message.c_str());
	} else if (serverMsgData->to == BZ_NULLUSER) {
	  if (serverMsgData->team == eAdministrators) {
	    bz_debugMessagef(0, "MSG-ADMIN 6:SERVER %s",
			     serverMsgData->message.c_str());
	  } else {
	    bz_debugMessagef(0, "MSG-TEAM 6:SERVER %s %s",
			     displayTeam( serverMsgData->team ).c_str(),
			     serverMsgData->message.c_str());
	  }
	} else {
	  bz_debugMessagef(0, "MSG-DIRECT 6:SERVER %s %s",
			   displayCallsign( serverMsgData->to ).c_str(),
			   serverMsgData->message.c_str());
	}
	break;
      case bz_ePlayerJoinEvent:
	{
	  if (joinPartData->record) {
	    bz_debugMessagef(0, "PLAYER-JOIN %s #%d%s %s %s",
			     displayCallsign( joinPartData->playerID).c_str(),
			     joinPartData->playerID,
			     displayBZid( joinPartData->playerID ).c_str(),
			     displayTeam( joinPartData->record->team ).c_str(),
			     displayPlayerPrivs( joinPartData->playerID ).c_str());
	    listPlayers( join, joinPartData);
	  }
	}
	break;
      case bz_ePlayerPartEvent:
	bz_debugMessagef(0, "PLAYER-PART %s #%d%s %s",
			 displayCallsign( joinPartData->playerID ).c_str(),
			 joinPartData->playerID,
			 displayBZid( joinPartData->playerID ).c_str(),
			 joinPartData->reason.c_str());
	listPlayers( part, joinPartData);
	break;
      case bz_ePlayerAuthEvent:
	bz_debugMessagef(0, "PLAYER-AUTH %s %s",
			 displayCallsign( authData->playerID ).c_str(),
			 displayPlayerPrivs( authData->playerID ).c_str()),
	listPlayers( join, joinPartData);
	break;
      default :
	break;
    }
  }
}
void leagueOverSeer::Init (const char* commandLine)
{
    bz_debugMessagef(0, "League Over Seer %i.%i.%i (%i) loaded.", MAJOR, MINOR, REV, BUILD);

    Register(bz_eCaptureEvent);
    Register(bz_eGameEndEvent);
    Register(bz_eGameStartEvent);
    Register(bz_eGetPlayerMotto);
    Register(bz_ePlayerJoinEvent);
    Register(bz_eTickEvent);

    bz_registerCustomSlashCommand("official", this);
    bz_registerCustomSlashCommand("fm",this);
    bz_registerCustomSlashCommand("finish",this);
    bz_registerCustomSlashCommand("cancel",this);
    bz_registerCustomSlashCommand("spawn",this);
    bz_registerCustomSlashCommand("resume",this);
    bz_registerCustomSlashCommand("pause",this);

    //Set all boolean values for the plugin to false
    officialMatch = false;
    doNotReportMatch = false;
    matchParticipantsRecorded = false;
    funMatch = false;
    teamOnePoints = 0;
    teamTwoPoints = 0;
    matchDuration = bz_getTimeLimit();
    matchStartTime = 0;
    matchRollCall = 90;

    loadConfig(commandLine); //Load the configuration data when the plugin is loaded

    if (mapchangePath != "" && rotLeague) //Check to see if the plugin is for a rotational league
    {
        //Open the mapchange.out file to see what map is being used
        std::ifstream infile;
        infile.open(mapchangePath.c_str());
        getline(infile,map);
        infile.close();

        map = map.substr(0, map.length() - 5); //Remove the '.conf' from the mapchange.out file

        bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Current map being played: %s", map.c_str());
    }

    teamOne = eNoTeam;
    teamTwo = eNoTeam;

    while (teamOne == eNoTeam || teamTwo == eNoTeam)
    {
        if (bz_getTeamPlayerLimit(eRedTeam) > 0 && teamOne == eNoTeam)
        {
            teamOne = eRedTeam;
            continue;
        }
        if (bz_getTeamPlayerLimit(eGreenTeam) > 0 && teamOne == eNoTeam)
        {
            teamOne = eGreenTeam;
            continue;
        }
        if (bz_getTeamPlayerLimit(eBlueTeam) > 0 && teamOne == eNoTeam)
        {
            teamOne = eBlueTeam;
            continue;
        }
        if (bz_getTeamPlayerLimit(ePurpleTeam) > 0 && teamOne == eNoTeam)
        {
            teamOne = ePurpleTeam;
            continue;
        }

        // Figure out the other team
        if (bz_getTeamPlayerLimit(eRedTeam) > 0 && teamOne != eRedTeam && teamTwo == eNoTeam)
        {
            teamTwo = eRedTeam;
            continue;
        }
        if (bz_getTeamPlayerLimit(eGreenTeam) > 0 && teamOne != eGreenTeam && teamTwo == eNoTeam)
        {
            teamTwo = eGreenTeam;
            continue;
        }
        if (bz_getTeamPlayerLimit(eBlueTeam) > 0 && teamOne != eBlueTeam && teamTwo == eNoTeam)
        {
            teamTwo = eBlueTeam;
            continue;
        }
        if (bz_getTeamPlayerLimit(ePurpleTeam) > 0 && teamOne != ePurpleTeam && teamTwo == eNoTeam)
        {
            teamTwo = ePurpleTeam;
            continue;
        }
    }

    bz_debugMessagef(0, "DEBUG :: League Over Seer :: Using the following database: %s", SQLiteDB.c_str());
    sqlite3_open(SQLiteDB.c_str(),&db);

    if (db == 0) //we couldn't read the database provided
    {
        bz_debugMessagef(0, "DEBUG :: League Over Seer :: Error! Could not connect to: %s", SQLiteDB.c_str());
        bz_debugMessage(0, "DEBUG :: League Over Seer :: Unloading League Over Seer plugin...");
        bz_unloadPlugin(Name());
    }

    if (db != 0) //if the database connection succeed and the database is empty, let's create the tables needed
        doQuery("CREATE TABLE IF NOT EXISTS [Players] (BZID INTEGER NOT NULL PRIMARY KEY DEFAULT 0, TEAM TEXT NOT NULL DEFAULT Teamless, SQUAD TEXT);");

    // Prepare the SQL query to get the team names based on a BZID
    getPlayerMotto = prepareQuery("SELECT team FROM players WHERE bzid = ?");

    updateTeamNames();
}
void leagueOverSeer::URLError(const char* /*URL*/, int errorCode, const char *errorString) //The server owner must have set up the URLs wrong because this shouldn't happen
{
    bz_debugMessage(DEBUG, "DEBUG :: League Over Seer :: Match report failed with the following error:");
    bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Error code: %i - %s", errorCode, errorString);
}
bool leagueOverSeer::SlashCommand(int playerID, bz_ApiString command, bz_ApiString /*message*/, bz_APIStringList *params)
{
    int timeToStart = atoi(params->get(0).c_str());
    bz_BasePlayerRecord *playerData = bz_getPlayerByIndex(playerID);

    if (command == "official") //Someone used the /official command
    {
        if (playerData->team == eObservers) //Observers can't start matches
            bz_sendTextMessage(BZ_SERVER, playerID, "Observers are not allowed to start matches.");
        else if ((bz_getTeamCount(eRedTeam) < 2 && bz_getTeamPlayerLimit(eRedTeam) > 0) ||
                (bz_getTeamCount(eGreenTeam) < 2 && bz_getTeamPlayerLimit(eGreenTeam) > 0) ||
                (bz_getTeamCount(eBlueTeam) < 2 && bz_getTeamPlayerLimit(eBlueTeam) > 0) ||
                (bz_getTeamCount(ePurpleTeam) < 2 && bz_getTeamPlayerLimit(ePurpleTeam) > 0)) //An official match cannot be 1v1 or 2v1
            bz_sendTextMessage(BZ_SERVER, playerID, "You may not have an official match with less than 2 players per team.");
        else if (funMatch) //A fun match cannot be declared an official match
            bz_sendTextMessage(BZ_SERVER,playerID,"Fun matches cannot be turned into official matches.");
        else if (!playerData->verified || !bz_hasPerm(playerID,"spawn")) //If they can't spawn, they aren't a league player so they can't start a match
            bz_sendTextMessage(BZ_SERVER,playerID,"Only registered league players may start an official match.");
        else if (bz_isCountDownActive() || bz_isCountDownInProgress()) //A countdown is in progress already
            bz_sendTextMessage(BZ_SERVER,playerID,"There is currently a countdown active, you may not start another.");
        else if (playerData->verified && playerData->team != eObservers && bz_hasPerm(playerID,"spawn") && !bz_isCountDownActive() && !funMatch) //Check the user is not an obs and is a league member
        {
            officialMatch = true; //Notify the plugin that the match is official
            bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Official match started by %s (%s).", playerData->callsign.c_str(), playerData->ipAddress.c_str());
            bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "Official match started by %s.", playerData->callsign.c_str());

            if (timeToStart <= 120 && timeToStart > 5)
                bz_startCountdown (timeToStart, bz_getTimeLimit(), "Server"); //Start the countdown with a custom countdown time limit under 2 minutes
            else
                bz_startCountdown (10, bz_getTimeLimit(), "Server"); //Start the countdown for the official match
        }
        else
            bz_sendTextMessage(BZ_SERVER, playerID, "You do not have permission to run the /official command.");

        return true;
    }
    else if (command == "fm") //Someone uses the /fm command
    {
        if (bz_isCountDownActive() || bz_isCountDownInProgress() || funMatch || officialMatch) //There is already a countdown
            bz_sendTextMessage(BZ_SERVER, playerID, "There is currently a countdown active, you may not start another.");
        else if (playerData->team == eObservers) //Observers can't start matches
            bz_sendTextMessage(BZ_SERVER,playerID,"Observers are not allowed to start matches.");
        else if (!playerData->verified || !bz_hasPerm(playerID,"spawn")) //If they can't spawn, they aren't a league player so they can't start a match
            bz_sendTextMessage(BZ_SERVER,playerID,"Only registered league players may start an official match.");
        else if (!bz_isCountDownActive() && playerData->team != eObservers && bz_hasPerm(playerID,"spawn") && playerData->verified && !officialMatch)
        {
            funMatch = true; //It's a fun match

            bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Fun match started by %s (%s).", playerData->callsign.c_str(), playerData->ipAddress.c_str());
            bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "Fun match started by %s.", playerData->callsign.c_str());

            if (timeToStart <= 120 && timeToStart > 5)
                bz_startCountdown (timeToStart, bz_getTimeLimit(), "Server"); //Start the countdown with a custom countdown time limit under 2 minutes
            else
                bz_startCountdown (10, bz_getTimeLimit(), "Server"); //Start the countdown for the official match
        }
        else
            bz_sendTextMessage(BZ_SERVER,playerID,"You do not have permission to run the /fm command.");

        return true;
    }
    else if (command == "cancel")
    {
        if (bz_hasPerm(playerID,"spawn") && bz_isCountDownActive())
        {
            if (officialMatch)
            {
                bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "Official match ended by %s", playerData->callsign.c_str());
                bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Match ended by %s (%s).", playerData->callsign.c_str(),playerData->ipAddress.c_str());
            }
            else
            {
                bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "Fun match ended by %s", playerData->callsign.c_str());
                bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Match ended by %s (%s).", playerData->callsign.c_str(),playerData->ipAddress.c_str());
            }

            //Reset the server. Cleanly ends a match
            officialMatch = false;
            doNotReportMatch = true;
            funMatch = false;
            teamOnePoints = 0;
            teamTwoPoints = 0;

            //End the countdown
            if (bz_isCountDownActive())
                bz_gameOver(253, eObservers);
        }
        else if (!bz_isCountDownActive())
            bz_sendTextMessage(BZ_SERVER, playerID, "There is no match in progress to cancel.");
        else //Not a league player
            bz_sendTextMessage(BZ_SERVER, playerID, "You do not have permission to run the /cancel command.");

        return true;
    }
    else if (command == "finish")
    {
        if (bz_hasPerm(playerID,"spawn") && bz_isCountDownActive() && officialMatch)
        {
            bz_debugMessagef(DEBUG, "DEBUG :: Match Over Seer :: Official match ended early by %s (%s)", playerData->callsign.c_str(), playerData->ipAddress.c_str());
            bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "Official match ended early by %s", playerData->callsign.c_str());

            doNotReportMatch = false; //To prevent reporting a canceled match, let plugin know the match was canceled

            //End the countdown
            if (bz_isCountDownActive())
                bz_gameOver(253, eObservers);
        }
        else if (!bz_isCountDownActive())
            bz_sendTextMessage(BZ_SERVER, playerID, "There is no match in progress to end.");
        else if (!officialMatch)
            bz_sendTextMessage(BZ_SERVER, playerID, "You cannot /finish a fun match. Use /cancel instead.");
        else //Not a league player
            bz_sendTextMessage(BZ_SERVER, playerID, "You do not have permission to run the /finish command.");

        return true;
    }
    else if (command == "pause")
    {
        if (bz_isCountDownActive() && bz_hasPerm(playerID,"spawn") && playerData->verified)
            bz_pauseCountdown(playerData->callsign.c_str());
        else if (!bz_isCountDownActive())
            bz_sendTextMessage(BZ_SERVER, playerID, "There is no active match to pause right now.");
        else
            bz_sendTextMessage(BZ_SERVER, playerID, "You are not have permission to run the /pause command.");

        return true;
    }
    else if (command == "resume")
    {
        if (bz_hasPerm(playerID,"spawn") && playerData->verified && bz_isCountDownActive())
            bz_resumeCountdown(playerData->callsign.c_str());
        else if (!bz_isCountDownActive())
            bz_sendTextMessage(BZ_SERVER, playerID, "The current match is not paused.");
        else
            bz_sendTextMessage(BZ_SERVER, playerID, "You are not have permission to run the /resume command.");

        return true;
    }
    else if (command == "spawn")
    {
        if (bz_hasPerm(playerID, "ban"))
        {
            if (params->size() > 0)
            {
                std::string callsignToLookup; //store the callsign we're going to search for

                for (unsigned int i = 0; i < params->size(); i++) //piece together the callsign from the slash command parameters
                {
                    callsignToLookup += params->get(i).c_str();
                    if (i != params->size() - 1) // so we don't stick a whitespace on the end
                        callsignToLookup += " "; // add a whitespace between each chat text parameter
                }

                if (std::string::npos != std::string(params->get(0).c_str()).find("#") && isValidPlayerID(atoi(std::string(params->get(0).c_str()).erase(0, 1).c_str())))
                {
                    bz_grantPerm(atoi(std::string(params->get(0).c_str()).erase(0, 1).c_str()), "spawn");
                    bz_sendTextMessagef(BZ_SERVER, eAdministrators, "%s gave spawn perms to %s", bz_getPlayerByIndex(playerID)->callsign.c_str(), bz_getPlayerByIndex(atoi(std::string(params->get(0).c_str()).substr(0, 1).c_str()))->callsign.c_str());
                }
                else if (isValidCallsign(callsignToLookup) >= 0)
                {
                    bz_grantPerm(isValidCallsign(callsignToLookup), "spawn");
                    bz_sendTextMessagef(BZ_SERVER, eAdministrators, "%s gave spawn perms to %s", bz_getPlayerByIndex(playerID)->callsign.c_str(), bz_getPlayerByIndex(isValidCallsign(callsignToLookup))->callsign.c_str());
                }
                else
                    bz_sendTextMessagef(BZ_SERVER, playerID, "player %s not found", params->get(0).c_str());
            }
            else
                bz_sendTextMessage(BZ_SERVER, playerID, "/spawn <player id or callsign>");
        }
        else if (!playerData->admin)
            bz_sendTextMessage(BZ_SERVER,playerID,"You do not have permission to use the /spawn command.");

        return true;
    }

    bz_freePlayerRecord(playerData);
    return false;
}
void leagueOverSeer::Event(bz_EventData *eventData)
{
    switch (eventData->eventType)
    {
        case bz_eCaptureEvent: //Someone caps
        {
            bz_CTFCaptureEventData_V1 *capData = (bz_CTFCaptureEventData_V1*)eventData;

            if (officialMatch) //Only keep score if it's official
            {
                if (capData->teamCapping == teamOne) teamOnePoints++;
                else if (capData->teamCapping == teamTwo) teamTwoPoints++;
            }
        }
        break;

        case bz_eGameEndEvent: //A /gameover or a match has ended
        {
            //Clear the bool variables
            funMatch = false;
            matchStartTime = 0;
            matchParticipantsRecorded = false;

            if (doNotReportMatch && officialMatch) //The match was canceled via /gameover or /superkill and we do not want to report these matches
            {
                officialMatch = false; //Match is over
                doNotReportMatch = false; //Reset the variable for next usage
                teamOnePoints = 0;
                teamTwoPoints = 0;
                matchPlayers.clear();
                bz_debugMessage(DEBUG, "DEBUG :: League Over Seer :: Official match was not reported.");
                bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, "Official match was not reported.");
            }
            else if (officialMatch)
            {
                officialMatch = false; //Match is over
                time_t t = time(NULL); //Get the current time
                tm * now = gmtime(&t);
                char match_date[20];

                sprintf(match_date, "%02d-%02d-%02d %02d:%02d:%02d", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); //Format the date to -> year-month-day hour:minute:second

                //Convert ints to std::string with std::ostringstream
                std::ostringstream teamOnePointsConversion;
                teamOnePointsConversion << (teamOnePoints);
                std::ostringstream teamTwoPointsConversion;
                teamTwoPointsConversion << (teamTwoPoints);
                std::ostringstream matchTimeConversion;
                matchTimeConversion << (matchDuration/60);

                //Keep references to values for quick reference
                std::string teamOnePointsFinal = teamOnePointsConversion.str();
                std::string teamTwoPointsFinal = teamTwoPointsConversion.str();
                std::string matchTimeFinal = matchTimeConversion.str();

                // Store match data in the logs
                bz_debugMessagef(DEBUG, "Match Data :: League Over Seer Match Report");
                bz_debugMessagef(DEBUG, "Match Data :: -----------------------------");
                bz_debugMessagef(DEBUG, "Match Data :: Match Time      : %s", match_date);
                bz_debugMessagef(DEBUG, "Match Data :: Duration        : %s", matchTimeFinal.c_str());
                bz_debugMessagef(DEBUG, "Match Data :: Team One Score  : %s", teamOnePointsFinal.c_str());
                bz_debugMessagef(DEBUG, "Match Data :: Team Two Score  : %s", teamTwoPointsFinal.c_str());

                // Start building POST data to be sent to the league website
                std::string matchToSend = "query=reportMatch";
                matchToSend += "&teamOneWins=" + std::string(bz_urlEncode(teamOnePointsFinal.c_str()));
                matchToSend += "&teamTwoWins=" + std::string(bz_urlEncode(teamTwoPointsFinal.c_str()));

                matchToSend += "&duration=" + std::string(bz_urlEncode(matchTimeFinal.c_str())) + "&matchTime=" + std::string(bz_urlEncode(match_date));

                if (rotLeague) //Only add this parameter if it's a rotational league such as Open League
                    matchToSend += "&mapPlayed=" + std::string(bz_urlEncode(map.c_str()));

                matchToSend += "&teamOnePlayers=";
                bz_debugMessagef(DEBUG, "Match Data :: Team One Players");

                for (unsigned int i = 0; i < matchPlayers.size(); i++) //Add all the red players to the match report
                {
                    if (matchPlayers.at(i).team == teamOne)
                    {
                        matchToSend += std::string(bz_urlEncode(matchPlayers.at(i).bzid.c_str())) + ",";
                        bz_debugMessagef(DEBUG, "Match Data ::  %s (%s)", matchPlayers.at(i).callsign.c_str(), matchPlayers.at(i).bzid.c_str());
                    }
                }

                matchToSend.erase(matchToSend.size() - 1);
                matchToSend += "&teamTwoPlayers=";
                bz_debugMessagef(DEBUG, "Match Data :: Team Two Players");

                for (unsigned int i = 0; i < matchPlayers.size(); i++) //Add all the red players to the match report
                {
                    if (matchPlayers.at(i).team == teamTwo)
                    {
                        matchToSend += std::string(bz_urlEncode(matchPlayers.at(i).bzid.c_str())) + ",";
                        bz_debugMessagef(DEBUG, "Match Data ::  %s (%s)", matchPlayers.at(i).callsign.c_str(), matchPlayers.at(i).bzid.c_str());
                    }
                }

                matchToSend.erase(matchToSend.size() - 1);

                bz_debugMessagef(DEBUG, "Match Data :: -----------------------------");
                bz_debugMessagef(DEBUG, "Match Data :: End of Match Report");
                bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Reporting match data...");
                bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, "Reporting match...");

                bz_addURLJob(LEAGUE_URL.c_str(), this, matchToSend.c_str()); //Send the match data to the league website

                //Clear all the structures and scores for next match
                matchPlayers.clear();
                teamOnePoints = 0;
                teamTwoPoints = 0;
            }
            else
                bz_debugMessage(DEBUG, "DEBUG :: League Over Seer :: Fun match was not reported.");
        }
        break;

        case bz_eGameStartEvent: //The countdown has started
        {
            if (officialMatch) //Don't waste memory if the match isn't official
            {
                //Set the team scores to zero just in case
                teamOnePoints = 0;
                teamTwoPoints = 0;
                matchDuration = bz_getTimeLimit();
                matchStartTime = bz_getCurrentTime();
                doNotReportMatch = false;
                matchPlayers.clear();
            }
        }
        break;

        case bz_eGetPlayerMotto: // Change the motto of a player when they join
        {
            bz_GetPlayerMottoData_V2* mottoEvent = (bz_GetPlayerMottoData_V2*)eventData;

            // Prepare the SQL query with the BZID of the player
            sqlite3_bind_text(getPlayerMotto, 1, mottoEvent->record->bzID.c_str(), -1, SQLITE_TRANSIENT);

            if (sqlite3_step(getPlayerMotto) == SQLITE_ROW) // If returns a team name, use it
                mottoEvent->motto = (const char*)sqlite3_column_text(getPlayerMotto, 0);
            else
                mottoEvent->motto = "";

            sqlite3_reset(getPlayerMotto); //Clear the prepared statement so it can be reused
        }

        case bz_ePlayerJoinEvent: //A player joins
        {
            bz_PlayerJoinPartEventData_V1 *joinData = (bz_PlayerJoinPartEventData_V1*)eventData;

            if (!joinData)
                return;

            if ((bz_isCountDownActive() || bz_isCountDownInProgress()) && officialMatch)
                bz_sendTextMessage(BZ_SERVER, joinData->playerID, "*** There is currently an official match in progress, please be respectful. ***");
            else if ((bz_isCountDownActive() || bz_isCountDownInProgress()) && funMatch)
                bz_sendTextMessage(BZ_SERVER, joinData->playerID, "*** There is currently a fun match in progress, please be respectful. ***");

            if (joinData->record->verified)
            {
                // Build the POST data for the URL job
                std::string teamMotto = "query=teamNameQuery";
                teamMotto += "&teamPlayers=" + std::string(joinData->record->bzID.c_str());

                bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Getting motto for %s...", joinData->record->callsign.c_str());

                bz_addURLJob(LEAGUE_URL.c_str(), this, teamMotto.c_str()); //Send the team update request to the league website
            }
        }
        break;

        case bz_eSlashCommandEvent: //Someone uses a slash command
        {
            bz_SlashCommandEventData_V1 *commandData = (bz_SlashCommandEventData_V1*)eventData;
            bz_BasePlayerRecord *playerData = bz_getPlayerByIndex(commandData->from);
            std::string command = commandData->message.c_str(); //Use std::string for quick reference

            if (strncmp("/gameover", commandData->message.c_str(), 9) == 0)
                bz_sendTextMessagef(BZ_SERVER, commandData->from, "** '/gameover' is disabled, please use /finish or /cancel instead **");
            else if (strncmp("/countdown pause", commandData->message.c_str(), 16) == 0)
                bz_sendTextMessagef(BZ_SERVER, commandData->from, "** '/countdown pause' is disabled, please use /pause instead **");
            else if (strncmp("/countdown resume", commandData->message.c_str(), 17 ) == 0)
                bz_sendTextMessagef(BZ_SERVER, commandData->from, "** '/countdown resume' is disabled, please use /resume instead **");
            else if (isdigit(atoi(commandData->message.c_str()) + 12))
                bz_sendTextMessage(BZ_SERVER, commandData->from, "** '/countdown TIME' is disabled, please use /official or /fm instead **");

            bz_freePlayerRecord(playerData);
        }
        break;

        case bz_eTickEvent: //Tick tock tick tock...
        {
            int totaltanks = bz_getTeamCount(eRogueTeam) + bz_getTeamCount(eRedTeam) + bz_getTeamCount(eGreenTeam) + bz_getTeamCount(eBlueTeam) + bz_getTeamCount(ePurpleTeam);

            if (totaltanks == 0)
            {
                //Incase a boolean gets messed up in the plugin, reset all the plugin variables when there are no players (Observers excluded)
                officialMatch = false;
                doNotReportMatch = false;
                funMatch = false;
                teamOnePoints = 0;
                teamTwoPoints = 0;

                //This should never happen but just incase the countdown is going when there are no tanks
                if (bz_isCountDownActive())
                    bz_gameOver(253, eObservers);
            }

            if (matchStartTime > 0 && matchStartTime + matchRollCall < bz_getCurrentTime() && officialMatch && !matchParticipantsRecorded)
            {
                bool invalidateRollCall = false;
                bz_APIIntList *playerList = bz_newIntList();
                bz_getPlayerIndexList(playerList);

                for (unsigned int i = 0; i < playerList->size(); i++)
                {
                    bz_BasePlayerRecord *playerRecord = bz_getPlayerByIndex(playerList->get(i));

                    if (bz_getPlayerTeam(playerList->get(i)) != eObservers) //If player is not an observer
                    {
                        playersInMatch currentPlayer;
                        currentPlayer.team = playerRecord->team; //Add team to structure
                        currentPlayer.callsign = playerRecord->callsign.c_str(); //Add team to structure
                        currentPlayer.bzid = playerRecord->bzID.c_str(); //Add bzid to structure

                        if (std::string(playerRecord->bzID.c_str()).empty())
                            invalidateRollCall = true;

                        matchPlayers.push_back(currentPlayer);
                    }

                    bz_freePlayerRecord(playerRecord);
                }

                bz_deleteIntList(playerList);

                if (invalidateRollCall && matchRollCall < matchDuration)
                {
                    bz_debugMessagef(DEBUG, "DEBUG :: League Over Seer :: Invalid player found on field at %i:%i.", (int)(matchRollCall/60), (int)(fmod(matchRollCall,60.0)));

                    matchParticipantsRecorded = false;
                    matchRollCall += 30;
                    matchPlayers.clear();
                }
                else
                    matchParticipantsRecorded = true;
            }
        }
        break;

        default:
        break;
    }
}