Click here to Skip to main content
15,881,248 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello, I'm currently developing an online game server(and the game itself) but I'm facing a difficulty.

If the game becomes popular and there are a million characters created(used and unused. there will always be characters not used by anyone anymore but I don't want to delete them incase their owner wants to return to the game), there will be issues.

Especially handling rank list of a million characters is a huge issue.

I want to know if there is a general method used by others or any other suggestions you guys have.

What I have tried:

I will try to explain the situation as clear as possible.

Just like many other projects, I plan on refreshing rank list with a specific time interval. I'm thinking of refreshing it every 5 minutes.


Ranks will be determined by users ELO(battle points, arena points, whatever you want to call it.)

After each fight , elo of winner and loser will be calculated and written to database.


And every 5 minutes I will execute below code to change ranks depending on user ELOs:
C++
boost::shared_ptr<pqxx::work> w = DBConnector::getWork();


//pqxx::result is an array of results that is returned by sql query.
pqxx::result r = w->exec("select * from characters order by elo desc");

for (int i = 0; i < r.size(); i++)
{
    long long userid = r[i]["userid"].as<long long>();
    int charid = r[i]["charid"].as<int>();

    w->exec("update characters set rank = " + std::to_string(i) + " where userid = " + std::to_string(userid) + " AND charid = " + std::to_string(charid));
}

w->commit();




The problem is, if there is a million characters, the whole process takes 2 minutes. This is too long.


What other alternatives do I have? Is there any solution that's used in general?
Posted
Updated 15-Oct-21 8:01am
v5

1. It would be faster to do the update in SQL, rather than pull all records into C++ and rewrite the data to the database.
2. If you are going to stick with C++, do not do SELECT *. Select just the specific field(s) you need.
3. Some games will group users together and then rank within that group.
 
Share this answer
 
Comments
Weird Japanese Shows 15-Oct-21 10:02am    
I did Select * for the sake of testing, ofcourse I will only load the needed parts. And no , I should have it in the server itself since I need to read that data frequently. But I feel like @Gret Utas 's solution will work out.
Let's say you have 1M characters and that each character requires 1KB of data. That's still only 1GB, so just shadow the entire database in memory and only do commits when necessary. Frequently re-importing the data after asking the database to sort it will necessarily be expensive.
 
Share this answer
 
Comments
Weird Japanese Shows 15-Oct-21 8:50am    
So I should put everything inside a vector of characterobject(a struct or a class which has needed variables to hold values in characters table) and when a data is needed from characters table I should take it from the vector and write the contents of the vector to database from a seperate thread. Right?

But doesn't that mean I still should lock the thread while I'm writing to database?(it will still take 4 minutes)
Greg Utas 15-Oct-21 8:57am    
A vector might be OK, but a list is better if you want to perform insert, erase, or sort operations.

If you only commit ELO changes, each write is independent. You can accumulate pending writes and batch commit them if that helps. Locking the database isn't necessary if only one thread interfaces to it.

Sorting by ELO would be done entirely in memory; there is no need for the external database to be sorted by ELO. When the game starts up, it starts by importing the database.
Weird Japanese Shows 15-Oct-21 10:00am    
Okay I got the gist of it, thanks alot for your advices!
Greg Utas 15-Oct-21 10:08am    
Dōitashimashite
The in memory operation is an excellent idea, but also try to optimize your code. Your string building in the exec statement isnt good. Better is too use one sprint or even better use all information as strings and so avoid type casting. Also use a variable for r[i] to minimize array operation.

Another point is that for the scoring only a reduced data set is used by the player id and his score. Everything else you dont need for that and so can be in another table/db.

Tip: use an stored procedure with some input data. Than the highly optimized db does the heavy lifting :-O
 
Share this answer
 
Comments
Weird Japanese Shows 15-Oct-21 15:30pm    
Oh I know , I really suck at code optimization! I didn't even know it would be heavier to use array operations instead of putting r[i] into a variable. Do you have any source for c++ code optimization to suggest ? If not, I may search for it randomly.

Actually I even thought about making the server code open source. So it can be useful for beginners. When I first started coding the server I couldn't find a proper example. And also that way it can be fixed bit by bit with the help of others.

You're suggesting I create an additional table just for scoring , right?
I didn't understand what you mean by "tip:use a stored procedure with some input data"

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900