Beispiel #1
0
bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, 
	uint32 char_id, DBTradeskillRecipe_Struct *spec)
{
	char errbuf[MYSQL_ERRMSG_SIZE];
    MYSQL_RES *result;
    MYSQL_ROW row;
    char *query = 0;
	char buf2[4096];
	
	uint32 sum = 0;
	uint32 count = 0;
	uint32 qcount = 0;
	uint32 qlen = 0;
	
 	// make where clause segment for container(s)
 	char containers[30];
 	if (some_id == 0) {
 		// world combiner so no item number
 		snprintf(containers,29, "= %u", c_type);
 	} else {
 		// container in inventory
 		snprintf(containers,29, "in (%u,%u)", c_type, some_id);
	}
	
	buf2[0] = '\0';
	
	//Could prolly watch for stacks in this loop and handle them properly...
	//just increment sum and count accordingly
	bool first = true;
	uint8 i;
	char *pos = buf2;
	for (i=0; i<10; i++) {
		const ItemInst* inst = container->GetItem(i);
		if (inst) {
			const Item_Struct* item = GetItem(inst->GetItem()->ID);
			if (item) {
				if(first) {
					pos += snprintf(pos, 19, "%d", item->ID);
					first = false;
				} else {
					pos += snprintf(pos, 19, ",%d", item->ID);
				}
				sum += item->ID;
				count++;
			}
		}
	}
	*pos = '\0';
	
	if(count < 1) {
		return(false);	//no items == no recipe
	}

	qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id "
	" FROM tradeskill_recipe_entries AS tre"
	" WHERE ( tre.item_id IN(%s) AND tre.componentcount>0 )"
 	"  OR ( tre.item_id %s AND tre.iscontainer=1 )"
 	" GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
 	"  AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum);

	if (!RunQuery(query, qlen, errbuf, &result)) {
		LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query);
		safe_delete_array(query);
		LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf);
		return(false);
	}
	safe_delete_array(query);
	
	qcount = mysql_num_rows(result);
	if(qcount > 1) {
		//multiple recipes, partial match... do an extra query to get it exact.
		//this happens when combining components for a smaller recipe
		//which is completely contained within another recipe
		
		first = true;
		pos = buf2;
		for (i = 0; i < qcount; i++) {
			row = mysql_fetch_row(result);
			uint32 recipeid = (uint32)atoi(row[0]);
			if(first) {
				pos += snprintf(pos, 19, "%u", recipeid);
				first = false;
			} else {
				pos += snprintf(pos, 19, ",%u", recipeid);
			}
			//length limit on buf2
			if(i == 214) { //Maximum number of recipe matches (19 * 215 = 4096)
				LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", i + 1, qcount);
				break;
			}
		}
		
		qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id"
		" FROM tradeskill_recipe_entries AS tre"
 		" WHERE tre.recipe_id IN (%s)"
 		" GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
 		"  AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum);
		
		if (!RunQuery(query, qlen, errbuf, &result)) {
			LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
			safe_delete_array(query);
			LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
			return(false);
		}
		safe_delete_array(query);
	
		qcount = mysql_num_rows(result);
	}
	
	if(qcount < 1)
		return(false);
	
	if(qcount > 1)
	{
		//The recipe is not unique, so we need to compare the container were using.

		uint32 containerId = 0;

		if(some_id) { //Standard container
			containerId = some_id;
		}
		else if(c_type) { //World container
			containerId = c_type;
		}
		else { //Invalid container
			return(false);
		}

		qlen = MakeAnyLenString(&query,"SELECT tre.recipe_id FROM tradeskill_recipe_entries as tre WHERE tre.recipe_id IN (%s)"
		" AND tre.item_id = %u;",buf2,containerId);
		
		if (!RunQuery(query, qlen, errbuf, &result)) {
			LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
			safe_delete_array(query);
			LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
			return(false);
		}
		safe_delete_array(query);

		uint32 resultRowTotal = mysql_num_rows(result); 
		
		if(resultRowTotal == 0) { //Recipe contents matched more than 1 recipe, but not in this container
			LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!");
			return(false);
		}
		if(resultRowTotal > 1) { //Recipe contents matched more than 1 recipe in this container
			LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", resultRowTotal, containerId);
		}
	}

	row = mysql_fetch_row(result);
	uint32 recipe_id = (uint32)atoi(row[0]);
	mysql_free_result(result);

	//Right here we verify that we actually have ALL of the tradeskill components..
	//instead of part which is possible with experimentation.
	//This is here because something's up with the query above.. it needs to be rethought out
	bool has_components = true;
	char TSerrbuf[MYSQL_ERRMSG_SIZE];
    char *TSquery = 0;
    MYSQL_RES *TSresult;
    MYSQL_ROW TSrow;
	if (RunQuery(TSquery, MakeAnyLenString(&TSquery, "SELECT item_id, componentcount from tradeskill_recipe_entries where recipe_id=%i AND componentcount > 0", recipe_id), TSerrbuf, &TSresult)) {
		while((TSrow = mysql_fetch_row(TSresult))!=NULL) {
			int ccnt = 0;
			for(int x = 0; x < 10; x++){
				const ItemInst* inst = container->GetItem(x);
				if(inst){
					const Item_Struct* item = GetItem(inst->GetItem()->ID);
					if (item) {				
						if(item->ID == atoi(TSrow[0])){
							ccnt++;
						}
					}
				}
			}
			if(ccnt != atoi(TSrow[1]))
				has_components = false;
		}
		mysql_free_result(TSresult);
	} else {
		LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", TSquery, TSerrbuf);			
	}
	safe_delete_array(TSquery);
	if(has_components == false){
		
		return false;
	}

	return(GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec));
}
Beispiel #2
0
bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id,
	uint32 char_id, DBTradeskillRecipe_Struct *spec)
{
	if (container == nullptr)
		return false;

	std::string containers;// make where clause segment for container(s)
	if (some_id == 0)
		containers = StringFormat("= %u", c_type); // world combiner so no item number
	else
		containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory

	//Could prolly watch for stacks in this loop and handle them properly...
	//just increment sum and count accordingly
	bool first = true;
	std::string buf2;
	uint32 count = 0;
	uint32 sum = 0;
	for (uint8 i = 0; i < 10; i++) { // <watch> TODO: need to determine if this is bound to world/item container size
		const ItemInst* inst = container->GetItem(i);
		if (!inst)
            continue;

        const Item_Struct* item = GetItem(inst->GetItem()->ID);
        if (!item)
            continue;

        if(first) {
            buf2 += StringFormat("%d", item->ID);
            first = false;
        } else
            buf2 += StringFormat(",%d", item->ID);

        sum += item->ID;
        count++;
	}

	if(count == 0)
		return false;	//no items == no recipe

	std::string query = StringFormat("SELECT tre.recipe_id "
                                    "FROM tradeskill_recipe_entries AS tre "
                                    "INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) "
                                    "WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) "
                                    "OR ( tre.item_id %s AND tre.iscontainer=1 ))"
                                    "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
                                    "AND sum(tre.item_id * tre.componentcount) = %u",
                                    buf2.c_str(), containers.c_str(), count, sum);
    auto results = QueryDatabase(query);
	if (!results.Success()) {
		Log.Out(Logs::General, Logs::Error, "Error in GetTradeRecipe search, query: %s", query.c_str());
		Log.Out(Logs::General, Logs::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str());
		return false;
	}

    if (results.RowCount() > 1) {
        //multiple recipes, partial match... do an extra query to get it exact.
        //this happens when combining components for a smaller recipe
        //which is completely contained within another recipe
        first = true;
        uint32 index = 0;
        buf2 = "";
        for (auto row = results.begin(); row != results.end(); ++row, ++index) {
            uint32 recipeid = (uint32)atoi(row[0]);
            if(first) {
                buf2 += StringFormat("%u", recipeid);
                first = false;
            } else
                buf2 += StringFormat(",%u", recipeid);

            //length limit on buf2
            if(index == 214) { //Maximum number of recipe matches (19 * 215 = 4096)
                Log.Out(Logs::General, Logs::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", index + 1, results.RowCount());
                break;
            }
        }

        query = StringFormat("SELECT tre.recipe_id "
                            "FROM tradeskill_recipe_entries AS tre "
                            "WHERE tre.recipe_id IN (%s) "
                            "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
                            "AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum);
		results = QueryDatabase(query);
        if (!results.Success()) {
            Log.Out(Logs::General, Logs::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str());
            Log.Out(Logs::General, Logs::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
            return false;
        }
    }

    if (results.RowCount() < 1)
        return false;

	if(results.RowCount() > 1) {
		//The recipe is not unique, so we need to compare the container were using.
		uint32 containerId = 0;

		if(some_id) //Standard container
			containerId = some_id;
		else if(c_type)//World container
			containerId = c_type;
		else //Invalid container
			return false;

		query = StringFormat("SELECT tre.recipe_id "
                            "FROM tradeskill_recipe_entries AS tre "
                            "WHERE tre.recipe_id IN (%s) "
                            "AND tre.item_id = %u;", buf2.c_str(), containerId);
        results = QueryDatabase(query);
		if (!results.Success()) {
			Log.Out(Logs::General, Logs::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str());
			Log.Out(Logs::General, Logs::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
			return false;
		}

		if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container
			Log.Out(Logs::General, Logs::Error, "Combine error: Incorrect container is being used!");
			return false;
		}

		if (results.RowCount() > 1) //Recipe contents matched more than 1 recipe in this container
			Log.Out(Logs::General, Logs::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", results.RowCount(), containerId);

	}

	auto row = results.begin();
	uint32 recipe_id = (uint32)atoi(row[0]);

	//Right here we verify that we actually have ALL of the tradeskill components..
	//instead of part which is possible with experimentation.
	//This is here because something's up with the query above.. it needs to be rethought out
	bool has_components = true;
	query = StringFormat("SELECT item_id, componentcount "
                        "FROM tradeskill_recipe_entries "
                        "WHERE recipe_id = %i AND componentcount > 0",
                        recipe_id);
	results = QueryDatabase(query);
    if (!results.Success()) {
        return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
    }

	if (results.RowCount() == 0)
        return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);

	for (auto row = results.begin(); row != results.end(); ++row) {
        int ccnt = 0;

        for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
            const ItemInst* inst = container->GetItem(x);
            if(!inst)
                continue;

            const Item_Struct* item = GetItem(inst->GetItem()->ID);
            if (!item)
                continue;

            if(item->ID == atoi(row[0]))
                ccnt++;
        }

        if(ccnt != atoi(row[1]))
            return false;
    }

	return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
}