Skip to content

Instantly share code, notes, and snippets.

@moiph
Forked from dkesberg/status.php
Last active February 11, 2016 12:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moiph/7977253 to your computer and use it in GitHub Desktop.
Save moiph/7977253 to your computer and use it in GitHub Desktop.
Server status / online players page for Starbound. Parses the log file to grab data. Naturally this won't scale with an ever increasing log file, but does the job for now! Hopefully Starbound will add server APIs to get the data in a more efficient manner :)
<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Linq" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Net.Sockets" %>
<script runat="server">
//// This is a replica of https://gist.github.com/dkesberg/7926899, but built for asp.net instead of PHP.
//// CONFIGURATION
// set logpath
const string logPath = @"C:\steamcmd\starbound\starbound_server.log";
const string serverIP = "YOUR IP HERE";
const int serverPort = 21025;
const int connectionTimeout = 5;
//// END CONFIGURATION
public long startTime;
public bool serverIsRunning = true;
public string version = "Unknown";
public IList<string> offlineClients;
public IList<string> onlineClients;
public Regex clientRx = new Regex("^Info:.*Client", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public Regex clientDetailsRx = new Regex(@".*?'(.*?)'.*?\(.*?\)(.*)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
startTime = DateTime.UtcNow.Millisecond;
// read the log file
var lines = new List<string>();
try
{
using (var fs = new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var sr = new StreamReader(fs))
{
while (!sr.EndOfStream)
{
lines.Add(sr.ReadLine());
}
}
}
}
catch (FileNotFoundException)
{
// no log file.
}
// filter down to just the lines regarding clients connecting/disconnecting
var linesToUse = lines.Where(l => clientRx.IsMatch(l));
var clients = new SortedDictionary<string, string>();
// the same client may disconnect/reconnect multiple times. we just want the latest status.
// could traverse backwards and ignore dupe keys instead but ultimately we need to walk through all the lines anyway.
foreach (string logClient in linesToUse)
{
Match clientMatch = clientDetailsRx.Match(logClient);
if (clientMatch.Success)
{
string clientName = HttpUtility.HtmlEncode(clientMatch.Groups[1].Value.Trim());
clients[clientName] = clientMatch.Groups[2].Value.Trim();
}
}
// only keep "connected" status
onlineClients = clients.Where(kvp => kvp.Value == "connected").Select(kvp => kvp.Key).ToList();
offlineClients = clients.Where(kvp => kvp.Value != "connected").Select(kvp => kvp.Key).ToList();
// get server version
var logVersion = lines.Where(s => s.StartsWith("Info: Server version"));
if (logVersion.Count() > 0)
{
string[] versionParts = logVersion.Last().Split(new char[] { '\'' });
if (versionParts.Length > 1)
{
version = versionParts[1];
}
}
// verify that the server is online
var ipe = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
var tempSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult result = tempSocket.BeginConnect(ipe, null, null);
bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(connectionTimeout), true);
if (success)
{
tempSocket.EndConnect(result);
}
else
{
tempSocket.Close();
serverIsRunning = false;
}
}
</script>
<!DOCTYPE html>
<html>
<head>
<title>Starbound Server Info</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<style type="text/css">
body {
padding-top: 70px;
}
table > tbody > tr > td.server-status {
vertical-align: middle;
}
</style>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Starbound Server Info</a>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-globe"></span> Server</div>
<div class="panel-body">
<table class="table table-condensed table-bordered">
<tbody>
<tr>
<th>Status</th>
<td class="server-status">
<span class="label label-<%= serverIsRunning ? "success" : "danger" %>">
<%= serverIsRunning ? "Online" : "Offline"%>
</span>
</td>
</tr>
<tr>
<th>Version</th>
<td><%= version %></td>
</tr>
<tr>
<th>IP</th>
<td><%= serverIP %></td>
</tr>
<tr>
<th>Players Online</th>
<td><%= onlineClients.Count() %></td>
</tr>
<tr>
<th>Players seen</th>
<td><%= onlineClients.Count() + offlineClients.Count() %></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-user"></span> Players</div>
<div class="panel-body">
<% if (onlineClients.Count() > 0)
{ %>
<table class="table table-condensed table-bordered">
<thead>
<tr>
<th>Playername</th>
</tr>
</thead>
<tbody>
<% foreach (var client in onlineClients)
{ %>
<tr>
<td>
<%= client %>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
No active players
<% } %>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<span class="label label-default">
Parse time: <%= DateTime.UtcNow.Millisecond - startTime %> ms.
</span>
</div>
</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment