Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[Release] Logging Unique Kills on a Scheduled Task (php)

Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
Video Guide:

I know there are a few others out there, but this is a bit different.

Features:
- Reads evaLogs / FatalLogs
- Can be ran more than once a day for frequent updating, without adding duplicates of the unique kills. Personally I'm running it every 5 minutes.
- Proceeded evaLogs / FatalLogs (of previous days) are moved to a folder of your choice (default "finished\")
- More flexible. The other ones I tried missed a few unique kills here and there. Hence I made this one :):
- Each unique kill is added to the database with the info of the killer, unique and the time.

Setting it up:
You will need the Microsoft SQL Driver for PHP (sqlsrv), it can be downloaded here:
Reason I use this is because it's maintained by Microsoft and works with SQL Server 2005 / 2008 and 2012

Creating the table:
PHP:
USE [SRO_VT_SHARD_INIT]
GO

/****** Object:  Table [dbo].[UniqueKills]    Script Date: 04/11/2012 23:06:39 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[UniqueKills](
	[CharName16] [varchar](20) NOT NULL,
	[Monster] [varchar](50) NOT NULL,
	[Timestamp] [varchar](12) NOT NULL
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

Creating the connection file:
Now we'll need to establish a connection to the SQL server.
I've created a PHP class specifically for the sqlsrv php extension to make basic queries and connections easier... It contains a few functions that isn't being used for the unique reader, but it might be useful for your other applications / web interface.
Put this in a separate file (config.php) and edit the username/password/database names (inside the __construct function) to match your sql server info

PHP:
<?php 
// By Flowlance (danny@leetcake.net)
class mssql {
	var $shard;
	var $account;
	var $params;
	
	function __construct() {
		// Database username
		$id = "username"; 
		// Database password
		$pw = "password";
		// Shard and Account database names
		$con_shard     = array("UID"=>$id, "PWD"=>$pw, "Database"=>"SRO_VT_SHARD_INIT"); 
		$con_account   = array("UID"=>$id, "PWD"=>$pw, "Database"=>"SRO_VT_ACCOUNT");
		// Shard And Account hostname / Connection
		$this->shard   = sqlsrv_connect("localhost", $con_shard) or die( print_r( sqlsrv_errors(), true));
		$this->account = sqlsrv_connect("localhost", $con_account) or die( print_r( sqlsrv_errors(), true));
		
		$this->params  = array("Scrollable" => SQLSRV_CURSOR_KEYSET);
	}
	
	function __destruct() {
		sqlsrv_close($this->shard);	
		sqlsrv_close($this->account);
	}
	
	public function db($db) {
		switch($db) {
			case 'SHARD':
				return $this->shard;
			break;
			
			case 'ACCOUNT':
				return $this->account;
			break;	
		}
	}
		
	public function query($db, $query, $values=array()) {
		$db = $this->db($db);
		$sql = sqlsrv_query($db, $query, $values, $this->params);
		
		if($query === false )
			return "Query error";
		else
			return $sql;
	}
	
	public function fetch($query) {
		return sqlsrv_fetch_array($query);
	}
	
	public function get_num($db, $query) {
		$db = $this->db($db);
		$sql = sqlsrv_query($db, $query, array(), $this->params);
		
		if($query === false )
			return var_dump(sqlsrv_errors());
		else
			return sqlsrv_num_rows($sql);
	}
	
	public function num($query) {
		return sqlsrv_num_rows($query);
	}
	
	public function last($query) {
		sqlsrv_next_result($query);
		sqlsrv_fetch($query);
		return sqlsrv_get_field($query, 0);
	}
	
	public function safe($data) {
		if ( !isset($data) or empty($data) ) return '';
		if ( is_numeric($data) ) return $data;
		$non_displayables = array(
			'/%0[0-8bcef]/',
			'/%1[0-9a-f]/',
			'/[\x00-\x08]/',
			'/\x0b/',
			'/\x0c/',
			'/[\x0e-\x1f]/'
		);
		foreach ( $non_displayables as $regex )
		$data = preg_replace( $regex, '', $data );
		$data = str_replace("'", "''", $data );
		return $data;
	}
}

$mssql = new mssql();
?>

Setting up the parser:
And for the parser itself, put this code in a separate file (unique_reader.php) and move it anywhere outside your web root to prevent web access.
At the very top of this file, you can edit:
- Timezone (get yours here )
- The path to your config.php file above (DBCONN)
- The table name where kills are being inserted (TABLE)
- Which log types you want to scan (LOGTYPE). Can be either 'EVALOGS' or 'FATALLOGS'
- Path to the directory of your log files (LOGPATH)
- Path to where proceeded log files are moved (DONEDIR). This folder will be created automatically in your log directory.
Do not forget the backslashes after a directory path.

PHP:
<?php
// By Flowlance (danny@leetcake.net)
date_default_timezone_set("Europe/Amsterdam"); // Your server's timezone

$settings = array(
	'DBCONN'  => 'C:\\inetpub\\xomnhala\\config.php', // Path to db connection file
	'TABLE'   => 'UniqueKills',                       // Unique Ranking table name
	'LOGTYPE' => 'EVALOGS',                           // "EVALOGS" or "FATALLOGS"
	'LOGPATH' => 'C:\\Server\\evaLog\\',              // Path to log files
	'DONEDIR' => 'finished\\'                         // Proceeded/Scanned logs will be moved here
);

include($settings['DBCONN']);

class UniqueReader extends mssql {
	var $table;
	var $logptrn;
	var $fileptrn;
	var $path;
	var $finished;
	var $logfiles;
	
	public function setVar($settings) {
		$this->table    = $settings['TABLE'];
		$this->logfiles = $settings['LOGTYPE'];
		$this->path     = $settings['LOGPATH'];
		$this->finished = $settings['DONEDIR'];
		
		if($this->logfiles == "EVALOGS") {
			$this->logptrn  = "/\[([0-9_\-]{1,})\].*\[([a-zA-Z0-9_]{1,})\].*\[([a-zA-Z0-9_]{1,})\]/siU";
			$this->fileptrn = "/^[0-9\-]{1,}\_uniquekills.txt$/";
		} else {
			$this->logptrn = "/([0-9_\-]{1,}\t[0-9_:]{1,})\t.*Unique\sMonster\sKilled!\sUNIQUE\[([a-zA-Z0-9_]{1,})\].*\sby\s\[([a-zA-Z0-9_]{1,})\]/siU";
			$this->fileptrn = "/^[0-9\-]{1,}\_FatalLog.txt$/";
		}
	}
	
	public function ts($datestr) {
		if($this->logfiles == "EVALOGS") {
			$split = split("_", $datestr);
			$date  = $split[0] . " " . str_replace("-", ":", $split[1]);
		} else {
			$date = str_replace("\t", " ", $datestr);
		}
		
		return strtotime($date);
	}
	
	public function match($search) {
		if(preg_match_all($this->logptrn, $search, $m)) {
			return $m;
		} else {
			return false;
		}
	}
	
	public function isToday($filename) {
		$split = split("_", $filename);
		$date  = $this->logfiles == "EVALOGS" ? date("j-n-Y") : date("Y-m-d");
		
		if($date==$split[0]) {
			return true;
		}
	}
	
	public function move($v) {
		if(!$this->isToday($v)) {
			if(copy($this->path . $v,$this->path . $this->finished . $v)) {
				unlink($this->path . $v);
			}
		}
	}
	
	public function folder_check() {
		if(!file_exists($this->path . $this->finished)) {
			mkdir($this->path . $this->finished);
		}
	}
	
	public function scan() {
		$this->folder_check();
		$files = scandir($this->path);

		foreach($files as $v) {
			if(preg_match($this->fileptrn, $v)) {
				$file = fopen($this->path . $v, "r");
				
				while($line = fgets($file)) {
					$cont = $this->match($line);
					if($cont!=false) {
						$exists = $this->get_num("SHARD", "SELECT * FROM ".$this->table." WHERE CharName16='".$this->safe($cont[3][0])."' AND Monster='".$this->safe($cont[2][0])."' AND Timestamp='".$this->ts($cont[1][0])."'");
						if($exists==0) {
							$params = array($this->safe($cont[3][0]), $this->safe($cont[2][0]), $this->ts($cont[1][0]));
							$this->query("SHARD", "INSERT INTO ".$this->table." (CharName16, Monster, Timestamp) VALUES (?, ?, ?)", $params);
						}
					}
				}
				
				fclose($file);
				$this->move($v);
			}
		}
	}
}

$reader = new UniqueReader();
$reader->setVar($settings);
$reader->scan();
?>


Running the program on a scheduled task:
- Start -> Search for "taskschd.msc"
- Click Create Task at the right hand side

General Tab:
- Name it whatever you want
- Under security options, check "Run whether user is logged on or not"

Triggers Tab:
- Begin the task: On a schedule
- Settings: Daily
- Recur every: 1 days
- Repeat task every: 5 minutes (or how often you want the rankings to be updated)

Flowlance - [Release] Logging Unique Kills on a Scheduled Task (php) - RaGEZONE Forums


Actions Tab:
- Action: Start a program
- Program/Script (Modify this to your php path): "C\PHP\v5.3\php.exe"
- Arguments (Modify this to your unique_reader.php path: C:\cronjobs\unique_reader.php

Flowlance - [Release] Logging Unique Kills on a Scheduled Task (php) - RaGEZONE Forums



Basic output page: (somefile.php)
- Just edit the config.php location at the top, and add which uniques you want to allow to the array. You can also edit their display names here.

PHP:
<?php
include("config.php");

// Add uniques you want to allow / display in the following array:
$allowed = array(
	'MOB_CH_TIGERWOMAN' => 'Tiger Girl',
	'MOB_OA_URUCHI'     => 'Uruchi',
	'MOB_KK_ISYUTARU'   => 'Isyutaru',
	'MOB_TK_BONELORD'   => 'Lord Yarkan',
	'MOB_EU_KERBEROS'   => 'Cerberus',
	'MOB_AM_IVY'        => 'Captain Ivy',
	'MOB_RM_TAHOMET'    => 'Demon Shaitan'
);

foreach($allowed as $k => $v) {
	$uniques = empty($uniques) ? "" : $uniques . ",";
	$uniques .= "'" . $k . "'";
}

echo "Top 15 Unique Killers:<br />\n";
$query = $mssql->query("SHARD", "SELECT TOP 15 CharName16, COUNT(*) AS Kills FROM UniqueKills WHERE Monster IN(".$uniques.") GROUP BY CharName16 ORDER BY Kills DESC");
while($row=$mssql->fetch($query)) {
	echo $row['CharName16'] . " - " . $row['Kills'] . " kills<br />\n";
}

echo "Last 15 Unique Kills:<br />\n";
$query = $mssql->query("SHARD", "SELECT TOP 15 * FROM UniqueKills WHERE Monster IN(".$uniques.") ORDER BY Timestamp DESC");
while($row=$mssql->fetch($query)) {
	echo $row['CharName16'] . " killed " . $allowed[$row['Monster']] . " at " . date("d-m-Y H:i", $row['Timestamp']) . "<br />\n";
}
?>

You should be all set.
If you need any help with setting it up / using the functions of the mssql class / other stuff.. Let me know,
Cheers
 
Last edited:
Newbie Spellweaver
Joined
Mar 3, 2012
Messages
54
Reaction score
5
Thanks very cool,but we could add also when one get points like creddy getting a custom title?
 
Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
Giving player a custom title depending on their amount of unique kills?
Exactly what I'm doing on my site, lol.
 
Junior Spellweaver
Joined
Dec 5, 2011
Messages
141
Reaction score
7
need help xD i did everthin but that thing appear

Warning: include(C:\AppServ\www 1\config.php) [function.include]: failed to open stream: Invalid argument in C:\AppServ\www\n1\index.php on line 2

Warning: include() [function.include]: Failed opening 'C:\AppServ\www 1\config.php' for inclusion (include_path='.;C:\php5\pear') in C:\AppServ\www\n1\index.php on line 2
Top 15 Unique Killers:

Fatal error: Call to a member function query() on a non-object in C:\AppServ\www\n1\index.php on line 20
 
Junior Spellweaver
Joined
Dec 5, 2011
Messages
141
Reaction score
7
<?php
include("C:\AppServ\www\n1\config.php");
// Add uniques you want to allow / display in the following array:
$allowed = array(
'MOB_CH_TIGERWOMAN' => 'Tiger Girl',
'MOB_OA_URUCHI' => 'Uruchi',
'MOB_KK_ISYUTARU' => 'Isyutaru',
'MOB_TK_BONELORD' => 'Lord Yarkan',
'MOB_EU_KERBEROS' => 'Cerberus',
'MOB_AM_IVY' => 'Captain Ivy',
'MOB_RM_TAHOMET' => 'Demon Shaitan'
);

foreach($allowed as $k => $v) {
$uniques = empty($uniques) ? "" : $uniques . ",";
$uniques .= "'" . $k . "'";
}

echo "Top 15 Unique Killers:<br />\n";
$query = $mssql->query("SHARD", "SELECT TOP 15 CharName16, COUNT(*) AS Kills FROM UniqueKills WHERE Monster IN(".$uniques.") GROUP BY CharName16 ORDER BY Kills DESC");
while($row=$mssql->fetch($query)) {
echo $row['CharName16'] . " - " . $row['Kills'] . " kills<br />\n";
}

echo "Last 15 Unique Kills:<br />\n";
$query = $mssql->query("SHARD", "SELECT TOP 15 * FROM UniqueKills WHERE Monster IN(".$uniques.") ORDER BY Timestamp DESC");
while($row=$mssql->fetch($query)) {
echo $row['CharName16'] . " killed " . $allowed[$row['Monster']] . " at " . date("d-m-Y H:i", $row['Timestamp']) . "<br />\n";
}
?>
we cant talk in skype ?
 
Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
Try escaping the backslashes in the include function

include("C:\\AppServ\\www\\n1\\config.php");

You can add me on skype (flowlance). I will be home later today
 
Junior Spellweaver
Joined
Dec 5, 2011
Messages
141
Reaction score
7
now this
Top 15 Unique Killers:

Warning: Missing argument 3 for mssql::query(), called in C:\AppServ\www\n1\index.php on line 20 and defined in C:\AppServ\www\n1\config.php on line 38
Last 15 Unique Kills:

Warning: Missing argument 3 for mssql::query(), called in C:\AppServ\www\n1\index.php on line 26 and defined in C:\AppServ\www\n1\config.php on line 38
 
Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
Which php version are you using?
In config.php, try editing

public function query($db, $query, $values) {

to

public function query($db, $query, $values=array()) {
 
Junior Spellweaver
Joined
Dec 5, 2011
Messages
141
Reaction score
7
AppServ .

now looks like this :)
Top 15 Unique Killers:
Last 15 Unique Kills:

and btw ppl already killed uniques and there's no thin in the website appear
 
Junior Spellweaver
Joined
Aug 31, 2010
Messages
131
Reaction score
91
Which php version are you using?
In config.php, try editing

public function query($db, $query, $values) {

to

public function query($db, $query, $values=array()) {

Just question why do you've 3 parameters for the connection stuff ? for the default query it requires 2 parameters which are($query , $connection)

even tho. $connection isn't needed I'll rewrite it as mssql and release it again but I need some one to contribute some logs.

Cheers,
Thief
 
Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
Just question why do you've 3 parameters for the connection stuff ? for the default query it requires 2 parameters which are($query , $connection)

even tho. $connection isn't needed I'll rewrite it as mssql and release it again but I need some one to contribute some logs.

Cheers,
Thief

The default sqlsrv query have 4 parameters, like this

sqlsrv_query($db, $query, $values, array("Scrollable" => SQLSRV_CURSOR_KEYSET));

i made the third one optional since it can be used for insert queries, as a placeholder for the parameters, like

$params = array($param1, $param2, $param3);
sqlsrv_query($db, "INSERT INTO table (CharName16, Monster, Timestamp) VALUES (?, ?, ?)", $params);

Mostly used for insert queries only.

@safty0202
Is the parser adding them to the database?
 
Junior Spellweaver
Joined
Aug 31, 2010
Messages
131
Reaction score
91
The default sqlsrv query have 4 parameters, like this

sqlsrv_query($db, $query, $values, array("Scrollable" => SQLSRV_CURSOR_KEYSET));

i made the third one optional since it can be used for insert queries, as a placeholder for the parameters, like

$params = array($param1, $param2, $param3);
sqlsrv_query($db, "INSERT INTO table (CharName16, Monster, Timestamp) VALUES (?, ?, ?)", $params);

Mostly used for insert queries only.

@safty0202
Is the parser adding them to the database?

that's why sqlsrv sux lol.
 
Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
I can write a class using mssql instead if that makes it easier. i like sqlsrv though, as it is fully supported with all sql server versions and have a lot of neat features. Harder yes.
Afaik mssql does not work with sql server 2008 or 2012 at all
 
Last edited:
Junior Spellweaver
Joined
Aug 31, 2010
Messages
131
Reaction score
91
I can write a class using mssql instead if that makes it easier. i like sqlsrv though, as it is fully supported with all sql server versions and have a lot of neat features. Harder yes.
Afaik mssql does not work with sql server 2008 or 2012 at all

Who started such rumors ?
 
Newbie Spellweaver
Joined
Mar 22, 2012
Messages
31
Reaction score
14
You've just to enable mssql extension correctly lol

Ya. I was referring to that it is "no longer supported and should not be used". It's the old way of doing it, however if it still works, that's fine I guess. A matter of personal choice.
Mssql support was dropped in php version 5.2 and is no longer included. The current version is 5.4.

Edit:
Added a video on how to set it up in the first post. Showing both evalogs and fatallogs
 
Last edited:
Experienced Elementalist
Joined
Sep 27, 2011
Messages
285
Reaction score
229
$allowed = array(
'MOB_CH_TIGERWOMAN' => 'Tiger Girl',
'MOB_OA_URUCHI' => 'Uruchi',
'MOB_KK_ISYUTARU' => 'Isyutaru',
'MOB_TK_BONELORD' => 'Lord Yarkan',
'MOB_EU_KERBEROS' => 'Cerberus',
'MOB_AM_IVY' => 'Captain Ivy',
'MOB_RM_TAHOMET' => 'Demon Shaitan'
);

Why dun you insert the textdata_object.txt file into a table and link it through a JOIN statement to the overview ;o so it'll cover all listed Uniques - doesn't matter the amount of new selfmade uniques
 
Back
Top