{"id":292,"date":"2008-01-12T18:33:39","date_gmt":"2008-01-12T22:33:39","guid":{"rendered":"http:\/\/www.rakkar.org\/blog\/?p=292"},"modified":"2008-01-12T18:33:39","modified_gmt":"2008-01-12T22:33:39","slug":"ranking-server-done","status":"publish","type":"post","link":"https:\/\/rakkar.org\/blog\/index.php\/2008\/01\/12\/ranking-server-done\/","title":{"rendered":"Ranking server done"},"content":{"rendered":"<p>\t\t\t\tI&#8217;ve finished writing a ranking server for <a HREF=\"http:\/\/www.rakkarsoft.com\">RakNet<\/a>. It is a C++ interface to a PostgreSQL database that stores a table that contains a list of matches between two participants. It&#8217;s very general &#8211; participants, games, scores, ratings, are just numbers. You can store a text string and arbitrary binary data for each match. It uses my functor system so calls are asynchronous.<\/p>\n<p>There are currently 4 functors:<br \/>\nSubmitMatch &#8211; Submits the match results between a pair of participants.<br \/>\nGetRatingForParticipant &#8211; Get the most recent rating for a single participants.<br \/>\nGetRatingForParticipants &#8211; Get the most recent rating for a list of participants.<br \/>\nGetHistoryForParticipant &#8211; Get all the matches and the data therein for a particular participant.<\/p>\n<p>I store IDs as a pair of unsigned integers. The intent is that the primary integer can represent a foreign key. The secondary integer can represent a sub-type. In Galactic Melee for example I store rankings for both players and squads, so the secondary integer would differentiate between these.<\/p>\n<p>The class is designed in such a way as to be easily extensible while still easy to use. Generally speaking the more extensible something is, the harder it is to use because there are more pieces involved, so you have a lot more to learn. This is true of Gamebryo for example, which has good division of code but then requires a lot of steps to do anything. I wanted to avoid this, so went to a lot of extra work to make as much automatic as possible.<\/p>\n<p>One nice thing is you can add more queries without changing the original source code. This is because each query is just a derived class.<\/p>\n<p>The current table format is:<\/p>\n<p>CREATE TABLE rankingServer (<br \/>\n\t\trowId serial UNIQUE,<br \/>\n\t\tparticipantADbId_primaryKey integer NOT NULL,<br \/>\n\t\tparticipantADbId_secondaryKey integer NOT NULL,<br \/>\n\t\tparticipantBDbId_primaryKey integer NOT NULL,<br \/>\n\t\tparticipantBDbId_secondaryKey integer NOT NULL,<br \/>\n\t\tparticipantAScore real,<br \/>\n\t\tparticipantBScore integer,<br \/>\n\t\tparticipantAOldRating real,<br \/>\n\t\tparticipantANewRating real NOT NULL,<br \/>\n\t\tparticipantBOldRating real,<br \/>\n\t\tparticipantBNewRating real NOT NULL,<br \/>\n\t\tgameDbId_primaryKey integer NOT NULL,<br \/>\n\t\tgameDbId_secondaryKey integer NOT NULL,<br \/>\n\t\tmatchTime bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM current_timestamp),<br \/>\n\t\tmatchNotes text,<br \/>\n\t\tmatchBinaryData bytea);<\/p>\n<p>Here&#8217;s an example of calling a Functor<\/p>\n<p><code><br \/>\nGetRatingForParticipants_PostgreSQLImpl *functor = GetRatingForParticipants_PostgreSQLImpl::Alloc();<\/p>\n<p>printf(\"Get the most recent rating for all participants\\n\");<\/p>\n<p>printf(\"Enter the primary ID of the game (int): \");<br \/>\ngets(inputStr);<br \/>\nfunctor->gameDbId.primaryKey = atoi(inputStr);<\/p>\n<p>printf(\"Enter the secondary ID of the game (int): \");<br \/>\ngets(inputStr);<br \/>\nfunctor->gameDbId.secondaryKey = atoi(inputStr);<\/p>\n<p>\/\/\/ Puts this functor on the processing queue. It will process sometime later in a thread.<br \/>\nrankingServer.PushFunctor(functor);<br \/>\n<\/code><\/p>\n<p>This is all part of my plan to write a complete lobby server, of which ranking and match tracking is one component.\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve finished writing a ranking server for RakNet. It is a C++ interface to a PostgreSQL database that stores a table that contains a list of matches between two participants. It&#8217;s very general &#8211; participants, games, scores, ratings, are just numbers. You can store a text string and arbitrary binary data for each match. It [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/posts\/292"}],"collection":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=292"}],"version-history":[{"count":0,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/posts\/292\/revisions"}],"wp:attachment":[{"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rakkar.org\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}