Some Codebase Questions

General forum for discussions regarding UTStatsDB
SkullCollector
Posts: 34
Joined: Sun Mar 15, 2026 8:57 am

Some Codebase Questions

Postby SkullCollector » Sat Mar 28, 2026 2:36 am

I have a few questions about the source code, and please don't take this as criticism; I'm just asking out of pure interest.

Why was the script written so that all the Unreal Engine components are processed in a single script?
imho : Which ones of the Games can be setup in the Config File. So if someone have all Servers running so set it up : ut2k4,ut99,ut3.
On serverside settings : give the server the flag : ut2k4 . So the codebase for 2k4 can be used. if ut3 , the codebase from ut3 can be used. and so on.

One file with all the functions in it. so you don't have to put these functions in the files.
one connect to the database , and make the stuff. The DBs today closes that by themself.

This makes the source code unnecessarily large. Some files are several thousand lines long. Furthermore, the HTML code is also generated within these files. Would a template engine like Smarty or something similar be much better suited? That way, the HTML and PHP code would be separated.

I know the project is several years old, but since you're already revising it, separating the HTML code from the PHP code would be a great way to do this.

This would keep the files smaller and more manageable.

I know that is much work for one Person to get it all working.


By the way : This is in my eyes the best Script i've seen so far. Some others are on the planet, but they don't have this ranking system.

so please keep this good peace of candy alive.

Thank you for your hard work.

Skully

SkullCollector
Posts: 34
Joined: Sun Mar 15, 2026 8:57 am

Re: Some Codebase Questions

Postby SkullCollector » Sat Mar 28, 2026 8:11 am

This is what i have done so far in the main.inc.php

Code: Select all

/** We now filter the superglobals like $_GET, $_POST, $_ENV, and $_REQUEST to verify the accuracy of the sent data. * Function call: filter_struct_utf8(1, $_GET or $_POST); * * @param integer $type Constant like INPUT_XXX. * @param array $default Default structure of the specified super global var. * Following bitmasks are available: * + FILTER_STRUCT_FORCE_ARRAY - Force 1 dimensional array. * + FILTER_STRUCT_TRIM - Trim by ASCII control chars. * + FILTER_STRUCT_FULL_TRIM - Trim by ASCII control chars, * full-width and no-break space. * @return array The value of the filtered super global var. */ define('FILTER_STRUCT_FORCE_ARRAY', 1); define('FILTER_STRUCT_TRIM', 2); define('FILTER_STRUCT_FULL_TRIM', 4); function filter_struct_utf8($type, array $default) { static $func = __FUNCTION__; static $trim = "[\\x0-\x20\x7f]"; static $ftrim = "[\\x0-\x20\x7f\xc2\xa0\xe3\x80\x80]"; static $recursive_static = false; if (!$recursive = $recursive_static) { $types = array( INPUT_GET => $_GET, INPUT_POST => $_POST, INPUT_COOKIE => $_COOKIE, ); if (!isset($types[(int) $type])) { throw new LogicException('unknown super global var type'); } $var = $types[(int) $type]; $recursive_static = true; } else { $var = $type; } $ret = array(); foreach ($default as $key => $value) { if ($is_int = is_int($value)) { if (!($value | ( FILTER_STRUCT_FORCE_ARRAY | FILTER_STRUCT_FULL_TRIM | FILTER_STRUCT_TRIM ))) { $recursive_static = false; throw new LogicException('unknown bitmask'); } if ($value & FILTER_STRUCT_FORCE_ARRAY) { $tmp = array(); if (isset($var[$key])) { foreach ((array) $var[$key] as $k => $v) { if (!preg_match('//u', $k)) { continue; } $value &= FILTER_STRUCT_FULL_TRIM | FILTER_STRUCT_TRIM; $tmp += array($k => $value ? $value : ''); } } $value = $tmp; } } if ($isset = isset($var[$key]) and is_array($value)) { $ret[$key] = $func($var[$key], $value); } elseif (!$isset || is_array($var[$key])) { $ret[$key] = null; } elseif ($is_int && $value & FILTER_STRUCT_FULL_TRIM) { $ret[$key] = preg_replace("/\A{$ftrim}++|{$ftrim}++\z/u", '', $var[$key]); } elseif ($is_int && $value & FILTER_STRUCT_TRIM) { $ret[$key] = preg_replace("/\A{$trim}++|{$trim}++\z/u", '', $var[$key]); } else { $ret[$key] = preg_replace('//u', '', $var[$key]); } if ($ret[$key] === null) { $ret[$key] = $is_int ? '' : $value; } } if (!$recursive) { $recursive_static = false; } return $ret; }
inside the php files you can make it like this :

Code: Select all

$params = array_merge(filter_struct_utf8(1, $_GET), filter_struct_utf8(1, $_POST), filter_struct_utf8(1, $_REQUEST));
so you don't need to call the function check_get or check_post.

i have done that in the playerstats.php like that :
instead of

Code: Select all

check_get($plr, "player");
i made this :

Code: Select all

$plr = intval($params["player"]);
and instead of :

Code: Select all

// Load Map Name $result2 = $sql_query("SELECT mp_name FROM {$dbpre}maps WHERE mp_num=$gm_map LIMIT 1"); if (!$result2) { echo "Map database error.<br />\n"; exit; } list($mp_name) = $result2->fetchRow(); $mapname = stripspecialchars($mp_name);
i use this :

Code: Select all

// Load Map Name $mapname = stripspecialchars($DB->GetOne("SELECT mp_name FROM {$dbpre}maps WHERE mp_num=$gm_map LIMIT 1")); if(!$mapname) { echo "Map database error.<br />\n"; exit; }
Do the same but has a smaller footprint.
i like the ADODB Database Layer- it makes some calls realy easy.

Regards
Skully

Panther
Site Admin
Posts: 530
Joined: Sat Dec 08, 2007 12:51 am
Contact:

Re: Some Codebase Questions

Postby Panther » Sat Mar 28, 2026 11:07 am

The software was written in 2003 for a single version...UT2003. It was a weekend project initially. When you ask about everything being in one script, I assume you mean the log parser. The original version was extremely basic, but then I wrote a more a more robust logger in C for the global stats system, which this is then based on. It wouldn't be too difficult to split some of the code into separate files, though most of it is still shared. There are plenty of places I'd rather spend my time optimizing though, such as more object oriented structures instead of just Player and MatchData. You also bring up the way the HTML code is handled, but that's not generated by the parser, and the main page doesn't really work with individual game types specificially. As for templates, I did start to implement a template system at some point, with the intent on moving most HTML to the templates, but never quite completed much of it.

I supposed I tend to look at a lot of the code as what's the most efficient, not what looks best in the source. Does collecting every passed variable and processing them through filters make the code run more efficiently than grabbing the single variable you're looking for and making sure it's an integer? That's how I look at it, but then, I started on extremely limited hardware. Still, there's plenty of optimization that can be done to allow for a more cleaned up main code, such as by this example, having a check_getint function that returns false if it's not an integer, or if it's out of range, such as negative.

SkullCollector
Posts: 34
Joined: Sun Mar 15, 2026 8:57 am

Re: Some Codebase Questions

Postby SkullCollector » Sat Mar 28, 2026 11:43 am

I think I misspoke. It's probably due to my poor English. Sorry for that.

If everything is the same for all Unreal types, then I understand why you handle everything in one place and don't write a separate codebase for each game type like UT99, UT2003, UT2004 and UT3.

OK, I understand why you didn't include a template engine, because it's a huge amount of work at the beginning. I once wrote a wiki for a company that was specifically tailored to their needs. Combining all of that with a template engine was really difficult at first. But once it was up and running, everyone was happy. :-)

Separating the HTML from the individual scripts isn't actually a problem; I've already done it in some files, and the file size has decreased slightly. It's a lot of work to do it on all pages.

Yes, I know that UTStatsDB is quite old. I experienced it firsthand when websites offering statistics for their UT servers were popping up everywhere.

I'm currently trying to figure out why headshots aren't being counted. Or rather, not always.

It seems to work when the server is running without mutators, because I have logged matches where headshots are displayed.

The log parser and its functions are still too complex for me; I haven't quite grasped everything yet :-)

I hate that , when i have to use a translator because of my poore English. Shame on me.

Skully

Panther
Site Admin
Posts: 530
Joined: Sat Dec 08, 2007 12:51 am
Contact:

Re: Some Codebase Questions

Postby Panther » Sat Mar 28, 2026 11:58 am

You brought up some good points of reducing repeated code, and that there is a lot of clean-up that can be done, but there is certainly the matter of how much time to spend on making things look better versus working better. I am still working on getting the new special event handling working properly, it's getting there. As for the different game types, it was more of that originally it was just one type (UT2003), then it was modified slightly to accomodate UT2004, then along came UT3 which had a number of differences and needed a little extra code to manage it, and then finally, I added support for UT '99. Or maybe UT '99 support came before UT3, I don't remember.

SkullCollector
Posts: 34
Joined: Sun Mar 15, 2026 8:57 am

Re: Some Codebase Questions

Postby SkullCollector » Mon Mar 30, 2026 1:44 am

well, i found some realy strange things.
chat_log.png
chat_log.png (68.3 KiB) Viewed 13 times
as you can see in the Screenshot, Headshots are shown. But not added to any player or to the Totals of UTStatsdb.
And i can't find a database table where this text is in.
ut_gtchat nothing with HEADSHOT in it.
I don't get it :-D
that cames out of the stats.*******.log file , but why the heck that is not tracked. Not for the totals and not for the player.
Parser reads it, but don't save it.

And by the Way : the Table ut_eventdesc is empty. I don't know how it is used by the UTStatsDB. I found one sql Query in a file that querys the eventdesc table.

Have i missed something ? in the Tablefile in tables/mysql dir is also empty. Just the Structure is in. :?:


Return to “UTStatsDB”