• Please make sure you are familiar with the forum rules. You can find them here: https://forums.tripwireinteractive.com/index.php?threads/forum-rules.2334636/

Code [Help] libHTTP4 help with ServerPerks

forrestmark9

Grizzled Veteran
Nov 29, 2011
1,110
42
This is probably a good question for Marco since he knows mostly how ServerPerks works but currently I'm having trouble setting up libHTTP4 to work with ServerPerks so far all I know is I need to add some new vars to ServerPerksMut then I need to spawn HttpSock.

So far all I know is to get someones stats I need to do something like this
Code:
Get("http://"$Mut.RemoteDatabaseURL$FMXServerPerksMut(Mut).HTTPFolder$ToSave[0].Name$".txt")
and to save someones stats to a tmp file is something like this
Code:
Post("http://"$Mut.RemoteDatabaseURL$FMXServerPerksMut(Mut).HTTPFolder$ToSave[0].Name$".txt.tmp", ToSave[0].GetSaveData());
So far that all I really know, what's confusing me on this I have no idea how to make it call get when a new player joins and get his stats
 
Last edited:
Afaik, this is how ServerPerks does it, since I did some research on player stat loading:

1. CheckReplacement()
-> Here, ServerPerks checks for PlayerController/PRI actors, since each PC/PRI represents 1 player in the server. It then adds the PC objects to a global array. For each PRI object, Tick is activated.

2. Tick()
-> Here, ServerPerks does required initializations like spawning LinkedRepInfos. The important bit here is the SetTimer, which will start the PC iteration

2. Timer()
-> Here, ServerPerks iterates through the PlayerController array earlier, and performs whatever functions required on each PC object. The array is then null-ed later. In the case of players joining late, the loop is started from CheckReplacement, however only iterating for the newer players.
**You should be calling LibHTTP4's Get function here!**

Here's a little snippet from our cosmetic shop mut (TBA), adapted from SVP:
Code:
function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
	if( ScrnPlayerController(Other)!=None )
	{
		PendingPlayers[PendingPlayers.Length] = ScrnPlayerController(Other);
		bSpawnLRI = true;
		//log("ScrnPlayerController != null");
	}

	if( PlayerReplicationInfo(Other) != None)	//get all PRI actors, so we can deal with them in Tick
	{
		PRI = PlayerReplicationInfo(Other);
		Enable('Tick');							//prepare to spawn LRI in Tick
	}

	if ( SRStatsBase(Other) != none ) {
        class'BrutusStatHandler'.static.InitStats(SRStatsBase(Other).Rep);
        //return true;
    }

	return true;
}

simulated function Tick( float DeltaTime )
{
	local LinkedReplicationInfo LRI, L;

    if ( !bTickExecuted )
	{
        bTickExecuted = true;
        FirstTickTime = Level.TimeSeconds;
    }

	if(bSpawnLRI)
	{
		//Spawn our LinkedReplicationInfo!
		LRI = Spawn(class'BrutusReplicationInfo', PRI.Owner);
		BrutusReplicationInfo(LRI).Mut = Self;	//create reference to ourself to be able to access our functions

		if(PRI.CustomReplicationInfo == None)
		{
			PRI.CustomReplicationInfo = LRI;
		}
		else
		{
			for( L=PRI.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
			{
				if( L.IsA('BrutusReplicationInfo') ) break;	//BRI already exists! we should not spawn again

				if( L.NextReplicationInfo == None )	//check if BRI exists in the LRI linked list
				{
					L.NextReplicationInfo = LRI; // Add to the end of the chain.
					break;
				}
			}
		}

		SetTimer(0.1,false);		//initialize replicationinfo data after spawning custom BRI!
		bSpawnLRI = false;
		Disable('Tick');
	}

	//Spawn Replication Infos here!

}

function Timer()
{
	local int i;

	//Checks if there are any players that have not have stats sent and send them
	for( i=(PendingPlayers.Length-1); i>=0; --i )
	{
		if( PendingPlayers[i]!=None && PendingPlayers[i].Player!=None)
		{
			SetPlayerData(PendingPlayers[i]);
			TimeLog(prefixInfo @ strPlayerJoin_Msg);
			TimeLog(prefixInfo @ Repl(strPlayerJoin_Name,"%d",PendingPlayers[i].PlayerReplicationInfo.PlayerName));
			TimeLog(prefixInfo @ Repl(strPlayerJoin_SteamID,"%d", PendingPlayers[i].GetPlayerIDHash()));
			WelcomeMessage(PendingPlayers[i]);
		}
	}
	PendingPlayers.Length = 0;
}
 
Upvote 0
Afaik, this is how ServerPerks does it, since I did some research on player stat loading:

Hmm interesting the database works slightly different it seems to spawned by the function RespawnNetworkLink() then that function calls BeginEvent in the Database class, in the FTPLink it sets a few things like delegates then goes into the idle stat where it seems to wait for PendingLoaders.Length>0 then it calls StartConnection() which then binds the needed port, then it enters the state InitConnection after that the FTPLink calls SendFTPLine when OpenNoSteam returns true then it connects to the FTP server
 
Last edited:
Upvote 0
Oh you were looking for the FTP implementation? I haven't really gotten around to look at that since the custom stat objects do a great job in holding all my custom data requirements. All I have to do is load it as shown and let SVP do all the netcode work. What extra vars are you looking at, for example?

EDIT:
Custom stats work fine with FTP btw, I have stuff like array info being saved via string masks and stuff over FTP.
 
Last edited:
Upvote 0
Oh you were looking for the FTP implementation? I haven't really gotten around to look at that since the custom stat objects do a great job in holding all my custom data requirements. All I have to do is load it as shown and let SVP do all the netcode work. What extra vars are you looking at, for example?

Hmm well I was just looking how FTP did it as HTTP is a similar case, the only extra vars I added are these

Code:
var transient HttpSock HttpLink;
var bool bUseHTTPLink;
var string HTTPFolder;
 
Upvote 0
Oh never mind, I just had a brief look at the new source, what Marco does is do RETR/STOR commands for retrieval/saving of FTP. Because FTP works abit like a command-line system, the Get function is probably a wrapper for RETR "remoteserverpath". Of course there are other bits behind which form the FTP protocol but the RETR/STOR are the main "commands" for file retrieval

Edit: LibHTTP doesnt seem to be used in this :), just uses TCPLink. Ingenious, Marco :p
 
Last edited:
Upvote 0
This was said by Wormbo on the matter, I know somewhat of what functions to call but not how to properly set things up

LibHTTP4 is quite easy to use for basic stuff. To make an HTTP request, you simply call the get(), head(), post() or postex() function on the HttpSock instance you spawned. The get() and head() functions make GET and HEAD requests, respectively. The difference is that a HEAD request expects only HTTP headers to be returned, but is otherwise identical to the GET request.

LibHTTP comes complete with support for authentication, cookies and proxies. It can also evaluate HTTP redirects via 3xx status codes. The content interpretation for returned data is left entirely up to you, though. For example for the UTAN Ban Manager v104 I created a ban database update protocol that allows the server to send ban data in a custom line-based format that contains instructions which bans to add or remove. Of course you could also use standard formats like JSON or XML to wrap your response or reply data. You will have to implement appropriate parsing, though.

If you want to send data, you usually pick from post() and postex(), although it is also possible to send data with the other request types. The difference between post() and postex() is that the former takes an optional string parameter for POST content, while the latter takes an optional string array parameter for the same purpose.

If you want to use the HTTP response, you should assign a callback function to the HttpSock's OnComplete delegate like in this example. The function will be called when the HTTP request completes successfully.

If the returned data is too large for single-pass processing, you might consider processing it as it comes in via the OnResponseBody delegate, which is called for each line of received response body (not headers) as they come in. If you need even more control, you could even extend from the HttpSock class. You will have to do that e.g. if you want to modify the HTTP UserAgent string.
 
Upvote 0
There, edited my post earlier. FTPTcpLink class of SVP7.1 is the class where you'll find all the necessary bits. You can also Grep through all the classfiles for FTPTcpLink to see how it's initialized and stuff. Windows Grep, nice GUI and fast for such purposes of reversing and understanding :D

FTP Technical Guide

Yes it does but how to incorporate this into libHTTP4 is beyond me, I'm not sure how ServerPerks gets the data from the .txt then sends it to the StatsObject

I'm speculating that the data is stored in PendingLoaders then sent to ToSave
 
Last edited:
Upvote 0
K err here's how it works:

NOTE: im not using the actual FTP Server responses, just for brevity
Client (game server) Server (FTP StatServer)

--FTP Initialization--
1) Login
Server sends a 220 username request
Client sends FTP server Username
Server sends 331 username successful, password request
Client sends FTP server Password
Server sends a 230 user logged in

2) Initialization
Client sends a CWD command to change working directory
Server sends 250 CWD successful
Client sends a TYPE A command to set file type as ASCII (note: TYPE I = binary, TYPE A = ascii, textfiles)
Server sends 200 Type change successful
Client sends PASV command to enter passive mode

3) File Request
On serverperks, you have the PendingLoaders array. After the Type Change above, it will start to iterate. For each PendingLoaders object, GetData(function of ServerStStats) is called with the downloaded data passed into it, via FTPDataConnection class.

FTPDataConnection class is populated this way:
In FTPTcpLink (DownloadStats event):
While still passive, FTPDataConnection is initialized
Client sends "RETR "$PendingLoaders[0].MyStatsObject.Name$".txt"
Server sends 150 open data connection
Server sends data
**NOTE**
At this point, the textfile is being downloaded into FTPDataConnection object as a string variable
Server sends 226 file successfully transferred

Code Execution returns to GetData, which probably parses the downloaded string into a stat object
 
Upvote 0