[WIP] Node.js UTStats Beta 8

Discussions about UT99
Wail
Novice
Posts: 3
Joined: Sat Sep 20, 2008 9:54 am

Re: nodeJS UTStats

Post by Wail » Sat Jun 15, 2019 1:02 am

This is a very cool project. I'm not aware of how all of this is working since I've never gotten into UT99 development. Are the stats reported by an existing module (e.g. UTStats.u) and this just communicates to your web service that stores all the data? Does this visualize the replays in any fashion, if so how does that work?

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Sat Jun 15, 2019 2:34 am

Events of matches are stored in log files from servers, and there are mutators like utstats and smart ctf that add more data to log files. This project reads the log files and imports the data inyo a database which then can be used in different ways like hraphs, replays and so on.

User avatar
rjmno1
Masterful
Posts: 601
Joined: Fri Aug 12, 2011 9:38 pm
Personal rank: masterfull

Re: nodeJS UTStats

Post by rjmno1 » Sun Jun 16, 2019 8:27 pm

UT Sniper (SJA94) wrote:Events of matches are stored in log files from servers, and there are mutators like utstats and smart ctf that add more data to log files. This project reads the log files and imports the data inyo a database which then can be used in different ways like hraphs, replays and so on.
Where is the data now wich website do you use.
Or are u still tweaking the program and needed a place for online usages?
Btw a very good project sind the ng worldstats went offline. :gj:
U must have a very large space to store this all,i dont know how big the website was who where holding the original ng worldstats.

https://web.archive.org/details/http:// ... stats.com/
unreal tournament 99
®
Image
Image
ImageImage

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Sun Jun 16, 2019 11:24 pm

I think you miss understood what I meant. With my system you can have a load of different servers connected to one database/website module without the need of having a different website for each server. It's not a service like 333networks for example, where you send data to a master database and view all the data of all the connected servers, although with some tweaking that can be possible without having to share private db information.


What I've tried to do is make it as easy and fast as possible to get everything set up, it's really easy to set it up so it will automatically import logs every x seconds/minutes without having to set up cron jobs or something similar. I also recently added an install script that does everything automatically for the server module, like creating the database, tables, directories, and so on.




Here is a test version online here: http://cut99.ddns.net:1337/

User avatar
rjmno1
Masterful
Posts: 601
Joined: Fri Aug 12, 2011 9:38 pm
Personal rank: masterfull

Re: nodeJS UTStats

Post by rjmno1 » Mon Jun 17, 2019 7:18 pm

UT Sniper (SJA94) wrote:I think you miss understood what I meant. With my system you can have a load of different servers connected to one database/website module without the need of having a different website for each server. It's not a service like 333networks for example, where you send data to a master database and view all the data of all the connected servers, although with some tweaking that can be possible without having to share private db information.


What I've tried to do is make it as easy and fast as possible to get everything set up, it's really easy to set it up so it will automatically import logs every x seconds/minutes without having to set up cron jobs or something similar. I also recently added an install script that does everything automatically for the server module, like creating the database, tables, directories, and so on.




Here is a test version online here: http://cut99.ddns.net:1337/
So it works differently but the main reason is the stats.You did a great job with that.
Maby you can add some search options also,searching for the names we all use in a different way on ut 99. :tu:
unreal tournament 99
®
Image
Image
ImageImage

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Wed Jun 19, 2019 5:02 am

I've been making smartCTF for match screenshots, I just added face image loading, and all an admin will have to do is save the faces to a folder and it will automatically load them now or default to faceless if taht certain face image doesn't exist.



Image

User avatar
esnesi
Adept
Posts: 448
Joined: Mon Aug 31, 2015 12:58 pm
Personal rank: on it!

Re: nodeJS UTStats

Post by esnesi » Wed Jun 19, 2019 9:39 pm

Looks nice and clean in the end result on the scoreboard! :gj:

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Thu Jun 20, 2019 7:18 am

After looking through the utstats uscript code, and license, I figure I will add a few small things(adding player faces loging, spawn kills) to go with my utstats system, I'm a noob at uscript but what I want to add seems simple enough to do.










----edit-------

Changed utstats mutator to save player faces including bots:

Image




---------- another edit -------------------

After messing around in uscript, I've decided to not modify the current utstats mutator and create my own little mutator without worrying about breaking stuff/stepping on peoples toes. So if I decided at a later point I want to change/add something it won't involve repackaging the whole utstats package.

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Mon Jun 24, 2019 7:44 am

I've been working on a mutator that will run alongside UTStats, so far it saves:
- Spawn kills,
- spawn kill sprees,
- best killing spree,
- best multi kill.
- Player Faces,
- Player Voices,
- Player netspeeds,
- Player fovs,
- Player MouseSens,
- Player DodgeClickTime


I would like to ask if I'm doing anything wrong with the code?

This is my first attempt at making a mutator, I've been scanning through the classes and learning by doing more than anything instead of doing tutorials.

I'm planning on adding support for MonsterHunt and Siege at some point.

https://github.com/scottadkin/NodeUTSta ... Mutator.uc

Code: Select all

//=============================================================================
// NodeUTStatsMutator.
//=============================================================================
class NodeUTStatsMutator expands Mutator;


var int CSP;

var (NodeUTStats) float SpawnKillTimeLimit;
var (NodeUTStats) float MultiKillTimeLimit;

struct nPlayer{
	var PlayerReplicationInfo p;
	var Pawn pawn;
	var int spawns;
	var float lastSpawnTime;
	var int id;
	var int spawnKills;
	var int spawnKillSpree;
	var int bestSpawnKillSpree;
	var float lastKillTime;
	var int currentSpree;
	var int bestSpree;
	var int currentMulti;
	var int bestMulti;
};


var nPlayer nPlayers[64];


function int getPlayerIndex(PlayerReplicationInfo p){

	local int i;

	for(i = 0; i < 64; i++){

		if(nPlayers[i].id == p.PlayerID){
			return i;
		}
	}

	return -1;
}


function int insertNewPlayer(Pawn p){
	
	local int i;
	local StatLog log;
	local int id;
	
	log = Level.Game.LocalLog;
	
	for(i = 0; i < 64; i++){
		
	
		if(nPlayers[i].id == -1){

			nPlayers[i].p = p.PlayerReplicationInfo;
			nPlayers[i].id = p.PlayerReplicationInfo.PlayerID;
			nPlayers[i].pawn = p;

			id = p.PlayerReplicationInfo.PlayerId;
			
			//LOG("Inseted new player "$p.PlayerName);
			log.LogEventString(log.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"Face"$Chr(9)$id$Chr(9)$nPlayers[i].p.TalkTexture);
			log.LogEventString(log.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"Voice"$Chr(9)$id$Chr(9)$nPlayers[i].p.VoiceType);
			log.LogEventString(log.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"NetSpeed"$Chr(9)$id$Chr(9)$PlayerPawn(p).Player.CurrentNetSpeed);
			log.LogEventString(log.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"Fov"$Chr(9)$id$Chr(9)$PlayerPawn(p).FovAngle);
			log.LogEventString(log.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"MouseSens"$Chr(9)$id$Chr(9)$PlayerPawn(p).MouseSensitivity);
			log.LogEventString(log.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"DodgeClickTime"$Chr(9)$id$Chr(9)$PlayerPawn(p).DodgeClickTime);
			

			return i;
		}
	}
}


function updateSpawnInfo(int offset){
		
	nPlayers[offset].spawns++;
	nPlayers[offset].lastSpawnTime = Level.TimeSeconds;

	//LOG(nPlayers[offset].p.PlayerName$" has spawned at "$nPlayers[offset].lastSpawnTime$" Total spawns = "$nPlayers[offset].spawns);

}


function PostBeginPlay(){

	local int i;

	LOG("��������������� NodeUTStats started ���������������");
	

	for(i = 0; i < 64; i++){
	
		nPlayers[i].id = -1;
		nPlayers[i].lastSpawnTime = -1;


	}
}

function bool HandleEndGame(){


	local int i;
	local StatLog log;

	for(i = 0; i < 64; i++){
		
		if(nPlayers[i].id == -1){
			break;
		}

		updateStats(i);
		updateSpecialEvents(i, true);
		log.LogEventString(log.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"SpawnKills"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].spawnKills);
		log.LogEventString(log.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"BestSpawnKillSpree"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].bestSpawnKillSpree);
		log.LogEventString(log.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"BestSpree"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].bestSpree);
		log.LogEventString(log.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"BestMulti"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].bestMulti);
	}

	if(NextMutator != None){
		return NextMutator.HandleEndGame();
	}

	return false;
}


function updateStats(int PlayerIndex){

	
	local int bestSpawnSpree;
	local int currentSpawnSpree;
	local int bestSpree;
	local int currentSpree;

	bestSpawnSpree = nPlayers[PlayerIndex].bestSpawnKillSpree;
	currentSpawnSpree = nPlayers[PlayerIndex].spawnKillSpree;
	bestSpree = nPlayers[PlayerIndex].bestSpree;
	currentSpree = nPlayers[PlayerIndex].currentSpree;


	if(currentSpawnSpree > bestSpawnSpree){

		nPlayers[PlayerIndex].bestSpawnKillSpree = currentSpawnSpree;

		Log(nPlayers[PlayerIndex].p.PlayerName$Chr(9)$" just got their best spawn kill spree ("$nPlayers[PlayerIndex].spawnKillSpree$") was ("$bestSpawnSpree$")");

		nPlayers[PlayerIndex].spawnKillSpree = 0;

	}

	if(currentSpree > bestSpree){
		LOG(nPlayers[PlayerIndex].p.PlayerName$" just beat their best killing spree "$currentSpree$" was ("$bestSpree$")");
		nPlayers[PlayerIndex].bestSpree = currentSpree;
	}
}


function UpdateSpecialEvents(int PlayerId, bool bKilled){

	local int bestMulti;
	local int currentMulti;
	local int bestSpree;
	local int currentSpree;
	local float lastKillTime;

	bestMulti = nPlayers[PlayerId].bestMulti;
	currentMulti = nPlayers[PlayerId].currentMulti;

	bestSpree = nPlayers[PlayerId].bestSpree;
	currentSpree = nPlayers[PlayerId].currentSpree;

	lastKillTime = nPlayers[PlayerId].lastKillTime;

	if(bKilled){
	
		nPlayers[PlayerId].currentMulti = 0;
		nPlayers[PlayerId].currentSpree = 0;

		if(currentSpree > bestSpree){
			nPlayers[PlayerId].bestSpree = currentSpree;
		}

		if(currentMulti > bestMulti){
			nPlayers[PlayerId].bestMulti = currentMulti;

		}

	}else{
	
		nPlayers[PlayerId].currentSpree++;

		if(Level.TimeSeconds - lastKillTime <= MultiKillTimeLimit){
			
			nPlayers[PlayerId].currentMulti++;

		}else{
			
			if(currentMulti > bestMulti){
				nPlayers[PlayerId].bestMulti = currentMulti;
			}

			nPlayers[PlayerId].currentMulti = 1;
		}

	}

}


function ScoreKill(Pawn Killer, Pawn Other){

	local int KillerId, OtherId;


	LOG(Other.Name);

	if(Killer.PlayerReplicationInfo != None){
		KillerId = getPlayerIndex(Killer.PlayerReplicationInfo);

	}else{
		KillerId = -1;
	}

	if(Other.PlayerReplicationInfo != None){
		OtherId = getPlayerIndex(Other.PlayerReplicationInfo);
		//LOG(Other.PlayerReplicationInfo);
	}else{
		OtherId = -1;
	}

	if(KillerId != -1){
		
		
		UpdateSpecialEvents(KillerId,false);

		nPlayers[KillerId].lastKillTime = Level.TimeSeconds;
		
		if(OtherId != -1){

			if(Level.TimeSeconds - nPlayers[OtherId].lastSpawnTime <= SpawnKillTimeLimit){
				//LOG("SPAWN KILLLLLL");
				nPlayers[KillerId].spawnKills++;
				nPlayers[KillerId].spawnKillSpree++;
				//nPlayers[KillerId].currentSpree++;
			}

			updateStats(OtherId);
			UpdateSpecialEvents(OtherId, true);

		}
	}


	if(OtherId != -1){
		//nPlayers[OtherId]
	}

	if(NextMutator != None){
		NextMutator.ScoreKill(Killer, Other);
	}

}

function ModifyPlayer(Pawn Other){

	local int currentPID;

	
	LOG(Other.PlayerReplicationInfo.Name);

	if(Other.PlayerReplicationInfo != None && Other.bIsPlayer){
		
		
		
		currentPID = getPlayerIndex(Other.PlayerReplicationInfo);

		if(currentPID == -1){

			currentPID = InsertNewPlayer(Other);
			//catch players that have killed themselves
			//updateStats(currentPID);
			//updateSpecialEvents(currentPID, true);

		}	

		updateStats(currentPID);
		updateSpecialEvents(currentPID, true);
		updateSpawnInfo(currentPID);
	}

	if (NextMutator != None)
      NextMutator.ModifyPlayer(Other);
}

defaultproperties
{
     SpawnKillTimeLimit=2.000000
     MultiKillTimeLimit=3.000000
}

--------edit----------


Just noticed a load of accessed nones that weren't there before.....

The_Cowboy
Average
Posts: 72
Joined: Mon Jan 24, 2011 3:22 am
Personal rank: Codezilla

Re: nodeJS UTStats

Post by The_Cowboy » Mon Jun 24, 2019 8:28 am

Is this thing

Code: Select all

nPlayers[i].id == -1
working alright? According to my memory, the default constructor of UnrealEngine sets int to 0. Maybe not set them to negative in the first place. What I do not remember is if the playerids begin from 0 or something else. Later Engine versions did and do support dynamic arrays hence lesser initialization time!

How does spawnkillspree work? I am assuming obtaining specific number of spawkills without getting killed yourself. If this is true, then there should be a corresponding code for counting spawnkills and resetting the spawnkillcounter. Besides that, rest looks good.

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Mon Jun 24, 2019 9:01 am

The_Cowboy wrote:Is this thing

Code: Select all

nPlayers[i].id == -1
working alright? According to my memory, the default constructor of UnrealEngine sets int to 0. Maybe not set them to negative in the first place. What I do not remember is if the playerids begin from 0 or something else. Later Engine versions did and do support dynamic arrays hence lesser initialization time!

How does spawnkillspree work? I am assuming obtaining specific number of spawkills without getting killed yourself. If this is true, then there should be a corresponding code for counting spawnkills and resetting the spawnkillcounter. Besides that, rest looks good.
nPlayers.id is a copy of PlayerReplicationInfo.PlayerId, or -1 if it's an unused nPlayer struct. When a new player has their first spawn the script looks for the next nPlayer struct with the id -1, then it changes the id to the pawns PlayerID, then adds a references to the pawns PlayerReplicationInfo(think it's unneeded now I changed some stuff).

Lol thanks for pointing out the spawnkillspree thing, atm it only resets it if the spawnkillspree is their best, I didn't notice :mrgreen:


Thanks for pointers.

I fixed all the accessed nones:

Code: Select all

//=============================================================================
// NodeUTStatsMutator.
//=============================================================================
class NodeUTStatsMutator expands Mutator;


var int CSP;

var (NodeUTStats) float SpawnKillTimeLimit;
var (NodeUTStats) float MultiKillTimeLimit;

struct nPlayer{
	var PlayerReplicationInfo p;
	var Pawn pawn;
	var int spawns;
	var float lastSpawnTime;
	var int id;
	var int spawnKills;
	var int spawnKillSpree;
	var int bestSpawnKillSpree;
	var float lastKillTime;
	var int currentSpree;
	var int bestSpree;
	var int currentMulti;
	var int bestMulti;
};


var nPlayer nPlayers[64];


function int getPlayerIndex(PlayerReplicationInfo p){

	local int i;

	for(i = 0; i < 64; i++){

		if(nPlayers[i].id == p.PlayerID){
			return i;
		}
	}

	return -1;
}


function int insertNewPlayer(Pawn p){
	
	local int i;
	//local StatLog log;
	local int id;


	
//	log = Level.Game.LocalLog;
	
	for(i = 0; i < 64; i++){
		
	
		if(nPlayers[i].id == -1){

			nPlayers[i].p = p.PlayerReplicationInfo;
			nPlayers[i].id = p.PlayerReplicationInfo.PlayerID;
			//nPlayers[i].pawn = p;

			//id = p.PlayerReplicationInfo.PlayerID;
			
			//LOG("Inseted new player "$p.PlayerName);

			if(nPlayers[i].p.TalkTexture != None){
				Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"Face"$Chr(9)$nPlayers[i].p.PlayerID$Chr(9)$nPlayers[i].p.TalkTexture);
			}

			if(nPlayers[i].p.VoiceType != None){
				Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"Voice"$Chr(9)$nPlayers[i].p.PlayerID$Chr(9)$nPlayers[i].p.VoiceType);
			}

			if(PlayerPawn(p) != None){

				Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"NetSpeed"$Chr(9)$nPlayers[i].p.PlayerID$Chr(9)$PlayerPawn(p).Player.CurrentNetSpeed);
				Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"MouseSens"$Chr(9)$nPlayers[i].p.PlayerID$Chr(9)$PlayerPawn(p).MouseSensitivity);
				Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"DodgeClickTime"$Chr(9)$nPlayers[i].p.PlayerID$Chr(9)$PlayerPawn(p).DodgeClickTime);
				Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.GetTimeStamp()$Chr(9)$"nstats"$Chr(9)$"Fov"$Chr(9)$nPlayers[i].p.PlayerID$Chr(9)$PlayerPawn(p).FovAngle);

			}
			
		
			

			
		
			return i;
		}
	}

	return -1;
}


function updateSpawnInfo(int offset){
		
	nPlayers[offset].spawns++;
	nPlayers[offset].lastSpawnTime = Level.TimeSeconds;

	//LOG(nPlayers[offset].p.PlayerName$" has spawned at "$nPlayers[offset].lastSpawnTime$" Total spawns = "$nPlayers[offset].spawns);

}


function PostBeginPlay(){

	local int i;

	LOG("��������������� NodeUTStats started ���������������");
	

	for(i = 0; i < 64; i++){
	
		nPlayers[i].id = -1;
		nPlayers[i].lastSpawnTime = -1;


	}
}

function bool HandleEndGame(){


	local int i;
	//local StatLog log;

	for(i = 0; i < 64; i++){
		
		if(nPlayers[i].id == -1){
			continue;
		}

		updateStats(i);
		updateSpecialEvents(i, true);
		Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"SpawnKills"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].spawnKills);
		Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"BestSpawnKillSpree"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].bestSpawnKillSpree);
		Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"BestSpree"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].bestSpree);
		Level.Game.LocalLog.LogEventString(Level.Game.LocalLog.getTimeStamp()$Chr(9)$"nstats"$Chr(9)$"BestMulti"$Chr(9)$nPlayers[i].id$Chr(9)$nPlayers[i].bestMulti);
	}

	if(NextMutator != None){
		return NextMutator.HandleEndGame();
	}

	return false;
}


function updateStats(int PlayerIndex){

	
	local int bestSpawnSpree;
	local int currentSpawnSpree;
	local int bestSpree;
	local int currentSpree;

	bestSpawnSpree = nPlayers[PlayerIndex].bestSpawnKillSpree;
	currentSpawnSpree = nPlayers[PlayerIndex].spawnKillSpree;
	bestSpree = nPlayers[PlayerIndex].bestSpree;
	currentSpree = nPlayers[PlayerIndex].currentSpree;


	if(currentSpawnSpree > bestSpawnSpree){

		nPlayers[PlayerIndex].bestSpawnKillSpree = currentSpawnSpree;

		Log(nPlayers[PlayerIndex].p.PlayerName$Chr(9)$" just got their best spawn kill spree ("$nPlayers[PlayerIndex].spawnKillSpree$") was ("$bestSpawnSpree$")");

		nPlayers[PlayerIndex].spawnKillSpree = 0;

	}

	if(currentSpree > bestSpree){
		LOG(nPlayers[PlayerIndex].p.PlayerName$" just beat their best killing spree "$currentSpree$" was ("$bestSpree$")");
		nPlayers[PlayerIndex].bestSpree = currentSpree;
	}
}


function UpdateSpecialEvents(int PlayerId, bool bKilled){

	local int bestMulti;
	local int currentMulti;
	local int bestSpree;
	local int currentSpree;
	local float lastKillTime;

	bestMulti = nPlayers[PlayerId].bestMulti;
	currentMulti = nPlayers[PlayerId].currentMulti;

	bestSpree = nPlayers[PlayerId].bestSpree;
	currentSpree = nPlayers[PlayerId].currentSpree;

	lastKillTime = nPlayers[PlayerId].lastKillTime;

	if(bKilled){
	
		nPlayers[PlayerId].currentMulti = 0;
		nPlayers[PlayerId].currentSpree = 0;

		if(currentSpree > bestSpree){
			nPlayers[PlayerId].bestSpree = currentSpree;
		}

		if(currentMulti > bestMulti){
			nPlayers[PlayerId].bestMulti = currentMulti;

		}

	}else{
	
		nPlayers[PlayerId].currentSpree++;

		if(Level.TimeSeconds - lastKillTime <= MultiKillTimeLimit){
			
			nPlayers[PlayerId].currentMulti++;

		}else{
			
			if(currentMulti > bestMulti){
				nPlayers[PlayerId].bestMulti = currentMulti;
			}

			nPlayers[PlayerId].currentMulti = 1;
		}

	}

}


function ScoreKill(Pawn Killer, Pawn Other){

	local int KillerId, OtherId;


	LOG(Other.Name);

	if(Killer.PlayerReplicationInfo != None){
		KillerId = getPlayerIndex(Killer.PlayerReplicationInfo);

	}else{
		KillerId = -1;
	}

	if(Other.PlayerReplicationInfo != None){
		OtherId = getPlayerIndex(Other.PlayerReplicationInfo);
		//LOG(Other.PlayerReplicationInfo);
	}else{
		OtherId = -1;
	}

	if(KillerId != -1){
		
		
		UpdateSpecialEvents(KillerId,false);

		nPlayers[KillerId].lastKillTime = Level.TimeSeconds;
		
		if(OtherId != -1){

			if(Level.TimeSeconds - nPlayers[OtherId].lastSpawnTime <= SpawnKillTimeLimit){
				//LOG("SPAWN KILLLLLL");
				nPlayers[KillerId].spawnKills++;
				nPlayers[KillerId].spawnKillSpree++;
				//nPlayers[KillerId].currentSpree++;
			}

			updateStats(OtherId);
			UpdateSpecialEvents(OtherId, true);

		}
	}


	if(OtherId != -1){
		//nPlayers[OtherId]
	}

	if(NextMutator != None){
		NextMutator.ScoreKill(Killer, Other);
	}

}

function ModifyPlayer(Pawn Other){

	local int currentPID;

	
	LOG(Other.PlayerReplicationInfo.Name);

	if(Other.PlayerReplicationInfo != None && Other.bIsPlayer){
		
		
		
		currentPID = getPlayerIndex(Other.PlayerReplicationInfo);

		if(currentPID == -1){

			currentPID = InsertNewPlayer(Other);
			//catch players that have killed themselves
			//updateStats(currentPID);
			//updateSpecialEvents(currentPID, true);

		}	
		
		if(currentPID != -1){
			updateStats(currentPID);
			updateSpecialEvents(currentPID, true);
			updateSpawnInfo(currentPID);
		}
	}

	if (NextMutator != None)
      NextMutator.ModifyPlayer(Other);
}

defaultproperties
{
     SpawnKillTimeLimit=2.000000
     MultiKillTimeLimit=3.000000
}



------------edit------------------

Image



------------edit-----------------

My goal for today is to add monsterhunt support, I've already made the mutator log kills on monsters:

Image

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Fri Jun 28, 2019 5:52 pm

Just thought I would share a screenshot of the start of monster stats, I've made it simple enough for anyone to add custom monster images by just having a JSON object with the monster class name and image location:

Code: Select all

const MonsterImages = [

    {"class": "unrealshare.brute", "image": "unrealshare.brute.png"},
    {"class": "unreali.behemoth", "image": "unrealshare.brute.png"},
    {"class": "unrealshare.lesserbrute", "image": "unrealshare.brute.png"},

    {"class": "unrealshare.cow", "image": "unrealshare.cow.png"},
    {"class": "unrealshare.babycow", "image": "unrealshare.cow.png"},

    {"class": "unrealshare.devilfish", "image": "unrealshare.devilfish.png"},

    {"class": "unrealshare.fly", "image": "unrealshare.fly.png"},

    {"class": "unreali.gasbag", "image": "unreali.gasbag.png"},
    {"class": "unreali.giantgasbag", "image": "unreali.gasbag.png"},

    {"class": "unreali.krall", "image": "unreali.krall.png"},
    {"class": "unreali.krallelite", "image": "unreali.krall.png"},
    {"class": "unreali.leglesskrall", "image": "unreali.krall.png"},

    {"class": "unrealshare.manta", "image": "unrealshare.manta.png"},
    {"class": "unrealshare.cavemanta", "image": "unrealshare.manta.png"},
    {"class": "unrealshare.giantmanta", "image": "unrealshare.manta.png"},

    {"class": "unreali.mercenary", "image": "unreali.mercenary.png"},
    {"class": "unreali.mercenaryelite", "image": "unreali.mercenary.png"},

    {"class": "unrealshare.nali", "image": "unrealshare.nali.png"},
    {"class": "unrealshare.nalipriest", "image": "unrealshare.nali.png"},

    {"class": "unreali.pupae", "image": "unreali.pupae.png"},
    {"class": "unreali.pupae", "image": "unreali.pupae.png"},
    // a few custom ones added here
    {"class": "pupae.energypupae", "image": "unreali.pupae.png"},
    {"class": "pupae.electropupae", "image": "unreali.pupae.png"},
    //------------------------------
    {"class": "unreali.queen", "image": "unreali.queen.png"},

    {"class": "unrealshare.skaarj", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjtrooper", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjgunner", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjinfantry", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjofficer", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjsniper", "image": "unrealshare.skaarj.png"},
    {"class": "unrealshare.skaarjwarrior", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.iceskaarj", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjassassin", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjberserker", "image": "unrealshare.skaarj.png"},
    {"class": "unreali.skaarjlord", "image": "unrealshare.skaarj.png"},
    {"class": "unrealshare.skaarjscout", "image": "unrealshare.skaarj.png"},

    {"class": "unrealshare.slith", "image": "unrealshare.slith.png"},

    {"class": "unreali.squid", "image": "unreali.squid.png"},

    {"class": "unrealshare.tentacle", "image": "unrealshare.tentacle.png"},


    {"class": "unreali.titan", "image": "unreali.titan.png"},
    {"class": "unreali.stonetitan", "image": "unreali.titan.png"},

    {"class": "unreali.warlord", "image": "unreali.warlord.png"},



];
Image

User avatar
Chamberly
Godlike
Posts: 1754
Joined: Sat Sep 17, 2011 4:32 pm
Personal rank: Dame. Vandora
Location: TN, USA
Contact:

Re: nodeJS UTStats

Post by Chamberly » Fri Jun 28, 2019 7:51 pm

Great job for a lot of stuff you have done. I've been quietly watching you doing improvement over the years and you are getting there.

Question tho, I notice you are using Windows OS, have you had it tested on Linux or plan to check it out in the future?

@KnoW , check this out.
Image
Image
Image

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Fri Jun 28, 2019 8:39 pm

I have a raspberry pi I can test it on, and can set up a vitual box on my main pc if needed.

Ive tried to make sure everthing I use will run fine on linux, expecially file names, thats why all map, face, and monster images are all lowercase. Im fully aware most people that will use this will be running linux, and have taken precautions from the start.

When ive done monsterhunt, I will start on bunnyyrack support.











------------edit----------------

I set up virtualbox, and installed linux, there were a few small problems most of it was me just being rusty with linux, but i've fixed the problems and both modules are working fine. (Biggest problem was me forgetting to update the install script after i added more database tables)



----------edit-----------------

Image

User avatar
UT Sniper (SJA94)
Masterful
Posts: 502
Joined: Thu Jun 24, 2010 10:35 pm
Personal rank: noob programmer
Location: England

Re: nodeJS UTStats

Post by UT Sniper (SJA94) » Sun Jul 07, 2019 7:06 pm

I've added a new ctf flag capture system:


Image

Post Reply